ChorusOS 4.0 Introduction

Basic Scheduling Control

The ChorusOS operating system provides two alternative ways of scheduling threads. These two features are mutually exclusive:

The default FIFO scheduler defines a pure priority-based, preemptive, FIFO (first-in first-out) policy. Priority of threads may vary from K_FIFO_PRIOMAX (0 and highest priority) to K_FIFO_PRIOMIN (255 and lowest priority). Within this policy, a thread which becomes ready to run after being blocked is always inserted at the end of its priority ready queue. A running thread is preempted only if a thread of a strictly higher priority becomes ready to run. A preempted thread is placed at the head of its priority queue, so that it will be selected when no other ready thread has a greater priority.

The ROUND_ROBIN feature is a general framework supporting simultaneous multiple scheduling policies or classes. The main classes dealt with here are the CLASS_FIFO and the CLASS_RR policies.

The CLASS_FIFO reproduces the behavior of the default scheduler policy precisely.

The CLASS_RR implements a priority-based preemptive policy with round-robin time slicing. Priority of threads may vary from K_RR_PRIOMAX to K_RR_PRIOMIN. It is similar to the default scheduler policy, except that an elected thread is given a fixed time quantum. If the thread is still running at quantum expiration, it is de-scheduled and placed at the end of its priority queue, thus yielding the processor to other threads of equal priority (if any).

It is possible to set scheduling attributes of threads at thread creation time (using the void* schedParams parameter of threadCreate()). It is also possible to get and modify scheduling attributes of a thread dynamically through the following call.

#include  <chorus.h>
#include  <sched/chFifo.h>
#include  <sched/chRr.h>
#include  <sched/chRt.h>
#include  <sched/chTs.h>
 
int threadScheduler(KnCap*      actorCap,
                    KnThreadLid thLi,
                    void*       oldParam,
                    void*       newParam);

This service enables you to get or set scheduling parameters of any thread of any actor, as long as both the actor capability and the thread identifier are known. threadScheduler() returns the current scheduling attributes of the target thread at the location defined by oldParam, if non-null. It will also set the attributes of the target thread according to the description provided at the location defined by newParam if non-null.

As the size, layout and semantics of scheduling parameters may vary depending on the scheduler configured in the system, or on the class of the ROUND_ROBIN framework, parameters are untyped in the generic interface definition. However, all scheduling parameter descriptions are similar, at least for the initial fields:

struct KnFifoThParms {
    KnSchedClass     fifoClass;      /* Always set to K_SCHED_FIFO */
    KnFifoPriority   fifoPriority;
} KnFifoThParms;

struct KnRrThParms {
    KnSchedClass     rrClass;        /* Always set to K_SCHED_RR */
    KnRrPriority     rrPriority;
} KnRrThParms;

The first field defines the scheduling policy applied or to be applied to the thread. The second field defines the priority of the thread within the scheduling policy.

Example 6-5 is based on the semaphore example, with a modification to the childCreate() routine so that it can receive scheduling attributes of the thread to be created. The main thread invokes this modified routine, so that the created thread will start as soon as it is created, rather than waiting for the main thread to yield the processor. Thus, the created thread must be given a higher priority than the main thread.

Refer to the threadScheduler(2K) and threadCreate(2K) man pages.


Example 6-5 Changing Scheduling Attributes

(file: progov/thSched.c)

#include <stdio.h>
#include <stdlib.h>
#include <chorus.h>

#define USER_STACK_SIZE (1024 * sizeof(long))

KnSem   sampleSem;

    int
childSchedCreate(KnPc entry, void* schedParams)
{
  KnActorPrivilege      actorP;
  KnDefaultStartInfo_f  startInfo;
  char*                 userStack;
  int                   childLid = -1;
  int                   res;

      /* Set defaults startInfo fields */
  startInfo.dsType            = K_DEFAULT_START_INFO;
  startInfo.dsSystemStackSize = K_DEFAULT_STACK_SIZE;

      /* Get actor's privilege */
  res = actorPrivilege(K_MYACTOR, &actorP, NULL);
  if (res != K_OK) {						      
    printf("Cannot get the privilege of the actor, error %d\n", res); 
    exit(1);							      
  }								      

      /* Set thread privilege */
  if (actorP == K_SUPACTOR) {
    startInfo.dsPrivilege = K_SUPTHREAD;
  } else {
    startInfo.dsPrivilege = K_USERTHREAD;
  }

      /* Allocate a stack for user threads */
  if (actorP != K_SUPACTOR) {
    userStack = malloc(USER_STACK_SIZE);
    if (userStack == NULL) {					      
      printf("Cannot allocate user stack\n");			      
      exit(1);							      
    }								      

    startInfo.dsUserStackPointer = userStack + USER_STACK_SIZE;
  } 

      /* Set entry point for the new thread */
  startInfo.dsEntry = entry;

      /* Create the thread in the active state */
  res = threadCreate(K_MYACTOR, &childLid, K_ACTIVE, schedParams, &startInfo);
  if (res != K_OK) {						      
    printf("Cannot create the thread, error %d\n", res);	      
    exit(1);						      	      
  }								      
								      
  return childLid;
}

    void
sampleThread()
{
  int myThreadLi;
  int res;

  myThreadLi = threadSelf();

  printf("I am the new thread. My thread identifier is: %d\n", myThreadLi);

  res  = semV(&sampleSem);
  if (res != K_OK){						      
    printf("Cannot perform the semV operation, error %d\n", res);     
    exit(1);							      
  }								      
  threadDelete(K_MYACTOR, K_MYSELF);
}

int main(int argc, char** argv, char**envp)
{

  int                  myThreadLi;
  int                  newThreadLi;
  int                  res;
  KnThreadDefaultSched schedParams;

  res = semInit(&sampleSem, 0);
  if (res != K_OK) {						      
    printf("Cannot initialize the semaphore, error %d\n", res);       
    exit(1);							      
  }								      

      /* acquire my own scheduling attributes  */
  res = threadScheduler(K_MYACTOR, K_MYSELF, &schedParams, NULL);

      /* Increase priority of thread to be created */
  schedParams.tdPriority -= 1;

  newThreadLi = childSchedCreate((KnPc)sampleThread, &schedParams);

  myThreadLi = threadSelf();

  printf("Parent thread identifier = %d, Child thread identifier = %d\n",
	 myThreadLi, newThreadLi);

  res = semP(&sampleSem, K_NOTIMEOUT);
  if (res != K_OK) {						      
    printf("Cannot perform the semP operation, error %d\n", res);     
    exit(1);							      
  }								      

  return 0;
}