Monday, December 31, 2018

Scheduling of Linux threads based on priorities.

Thread priorities do work on most OS's but they often have minimal effect. Priorities help to order the threads that are in the run queue only and will not change how often the threads are run in any major may unless you are doing a ton of CPU in each of the threads.
Unless you have fewer cores than there are threads, you may not see any change in output order by setting your thread priorities. If there is a free CPU then even a lower priority thread will be scheduled to run.
Also, threads are never starved. Even a lower priority thread will be given time to run quite often in such a situation as this. You should see higher priority threads be given time sliced to run more often but it does not mean lower priority threads will wait for them to finish before running themselves.
Even if priorities do help to give one thread more CPU than the others, threaded programs are subject to race conditions which help inject a large amount of randomness to their execution. What you should see, is the max priority thread is more likely to spit out its message more often than the rest.

In modern computers, many processes run at once. Active processes are placed in an array called a run queue. The run queue may contain priority values for each process, which will be used by the scheduler to determine which process to run next
To ensure each program has a fair share of resources, each one is run for some time period (quantum) before it is paused and placed back into the run queue. When a program is stopped to let another run, the program with the highest priority in the run queue is then allowed to execute.

Processes are also removed from the run queue when they ask to sleep, are waiting on a resource to become available, or have been terminated.

In the Linux operating system (prior to kernel 2.6.23), each CPU in the system is given a run queue, which maintains both an active and expired array of processes. Each array contains 140 (one for each priority level) pointers to doubly linked lists, which in turn reference all processes with the given priority. The scheduler selects the next process from the active array with the highest priority. When a process' quantum expires, it is placed into the expired array with some priority. When the active array contains no more processes, the scheduler swaps the active and expired arrays, hence the name O(1) scheduler.



In UNIX or Linux, the sar command is used to check the run queue.

The vmstat UNIX or Linux command can also be used to determine the number of processes that are queued to run or waiting to run. These appear in the 'r' column.


There are two models for Run queues: one that assigns a Run Queue to each physical processor, and the other has only one Run Queue in the system


Why do we get random results (every time I run it changes):
The output from a threaded program is rarely if ever "perfect" because by definition the threads are running asynchronously. We want the output to be random because we want the threads to be running in parallel independently from each other. That is their power. If you expecting some precise output then you should not be using threads. Still, the following program will give you the desired result every time you run it.
#define _GNU_SOURCE // must be placed prior to sched.h
#include <stdio.h>
#include <linux/errno.h>
#include <pthread.h>
#include <sched.h>
#include <unistd.h>
#include <stdio.h>

void *thread1(void *null)
{
  int policy;
  struct sched_param param;

  pthread_getschedparam(pthread_self(), &policy, &param);
  // 0 - SCHED_OTHERS     1 - SCHED_FIFO          2 - SCHED_RR
  printf("\n thread 1 Current Thread Policy : %d Priority : %d\n", policy,
         param.sched_priority);

  sleep(5);
  pthread_getschedparam(pthread_self(), &policy, &param);
  // 0 - SCHED_OTHERS     1 - SCHED_FIFO          2 - SCHED_RR
  printf("\n thread 1 Current Thread Policy : %d Priority : %d\n", policy,
         param.sched_priority);
  __fpurge();
  pthread_exit(NULL);
}
void * thread2 (void *null)
{
  //printf("i am thread 2\n");
  int policy;
  struct sched_param param;

  pthread_getschedparam(pthread_self(), &policy, &param);
  // 0 - SCHED_OTHERS     1 - SCHED_FIFO          2 - SCHED_RR
  printf("\n thread 2 Current Thread Policy : %d Priority : %d\n", policy,
         param.sched_priority);
  sleep(5);
  pthread_getschedparam(pthread_self(), &policy, &param);
  // 0 - SCHED_OTHERS     1 - SCHED_FIFO          2 - SCHED_RR
  printf("\n thread 2 Current Thread Policy : %d Priority : %d\n", policy,
         param.sched_priority);
  __fpurge();
  pthread_exit(NULL);

}
int main()
{
  int inherit, policy, rc , status;
  pthread_t t1,t2;
  pthread_attr_t attr;
  struct sched_param param;

  pthread_attr_init(&attr);
  /* switch off sched inheritence from parent */
  pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

  /* Set processor affinity */
  cpu_set_t mask;
  CPU_ZERO(&mask); // Clears set, so that it contains no CPUs.
  CPU_SET (1, &mask);  /* use only 1 CPU core */
  unsigned int len = sizeof(mask);
  status = sched_setaffinity(0, len, &mask);
  if (status < 0) perror("sched_setaffinity");
  status = sched_getaffinity(0, len, &mask);
  if (status < 0) perror("sched_getaffinity");


  /* Assign Sched policy and priority */
  policy = SCHED_FIFO;
  pthread_attr_setschedpolicy(&attr, policy);

  param.sched_priority = 10;
  pthread_attr_setschedparam(&attr, &param);

  /* create thread with choosen attribs */
  pthread_create(&t1, &attr, thread1, NULL);// t1 ie thread object gets initialised here
  pthread_create(&t2, &attr, thread2, NULL);
  /* destroy attribute object */
  pthread_attr_destroy(&attr);
  sleep(2);

/* FOR thread 2 */
  param.sched_priority = 17;
  policy = SCHED_RR;
  pthread_setschedparam(t2, policy, &param);

  /* FOR thread 1 */
  param.sched_priority = 16;
  //policy = SCHED_RR;
  pthread_setschedparam(t1, policy, &param);

  pthread_exit(NULL); // wait for all threads to terminate.

}


References:
1. http://www.informit.com/articles/article.aspx?p=101760&seqNum=2
2. https://en.wikipedia.org/wiki/Run_queue