ChorusOS 4.0 Introduction

Thread Handling

Getting a Thread Identifier

A thread may obtain its local identifier by means of the following ChorusOS operating system service:

#include <chorus.h>

int threadSelf();

An example of how this call can be used is provided in Example 6-1.

Creating a Thread

A thread may be created dynamically by means of the following ChorusOS operating system service:

#include <chorus.h>

int threadCreate(KnCap*         actorCap, 
                 KnThreadLid*   thLi, 
                 KnThreadStatus status,
                 void*          schedParam, 
                 void*          startInfo);

The actorCap parameter identifies the actor in which the new thread will be created. You can create the new thread in the current actor by passing K_MYACTOR as the actor capability. This is the usual case. Should this be successful, the local identifier of the newly created thread is returned at the location defined by the thLi parameter.

The schedParam parameter is used to define the scheduling properties of the thread to be created. If this parameter is set to 0, the created thread inherits the scheduling attributes of the creator thread.

The startInfo parameter is used to define the initial state of the thread, such as the initial program counter of the thread (the thread entry point), as well as the initial value of the stack pointer to be used by the created thread. You can also define whether the thread will run as a user thread or as a supervisor thread.

A thread needs a stack to run, in order to have room to store its local variables. When the thread is a user thread, the user must explicitly provide a stack to the thread. However, stacks for supervisor threads are implicitly allocated by the system. In fact, a system stack is allocated for all threads, even those running in user mode.


Note -

As the operating system does not prevent the user stack from overflowing, checks must be made every time a thread is created.


System stacks are not allowed to overflow as memory will become corrupted, resulting in unpredictable operating system behavior.

Example 6-1 is a simple program illustrating the creation of a thread by the main thread of an actor. The actor is loaded by the arun command. Its main thread is implicitly created by the system. The goal of the example is to:

This example will work without modification whether it is run as a user or as a supervisor actor. In the first case, a user thread must be created, while in the second case a supervisor thread must be created. Using the actorPrivilege() service call might be helpful for this purpose.

This example requires some kind of synchronization between the main thread and the created one. Execution of a thread can be suspended for a given delay:

#include <chorus.h>

int threadDelay(KnTimeVal* waitLimit);

This call suspends the execution of the invoking thread for a period specified by the KnTimeVal structure (see "Current Time " for more detail). There are two predefined values:

These values may be used instead of the pointer to the KnTimeVal data structure. There is also a predefined macro which sets such a structure from a delay expressed in milliseconds: K_MILLI_TO_TIMEVAL(KnTimeVal* waitLimit, int delay). For more information, see the threadCreate(2K), threadDelay(2K), and threadSelf(2K) man pages.


Example 6-1 Creating a Thread

(file: progov/thCreate.c)

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

#define USER_STACK_SIZE (1024 * sizeof(long))

    int
childCreate(KnPc entry)
{
  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, 0, &startInfo);
  if (res != K_OK) {						      
    printf("Cannot create the thread, error %d\n", res);	      
    exit(1);							      
  }								      
								      
  return childLid;
}

    void
sampleThread()
{
  int myThreadLi;

  myThreadLi = threadSelf();

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

      /* Block itself for ever */
  threadDelay(K_NOTIMEOUT);
}


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

  int        myThreadLi;
  int        newThreadLi;
  int        res;						      
  KnTimeVal  wait;

  newThreadLi = childCreate((KnPc)sampleThread);

  myThreadLi = threadSelf();

      /* Initialize KnTimeVal structure */
  K_MILLI_TO_TIMEVAL(&wait, 10);

      /*
       * Suspend myself for 10 milliseconds to give the newly
       * created thread the opportunity to run before
       * the actor terminates.
       */
  res = threadDelay(&wait);

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

  return 0;
}


Deleting a Thread

A thread may be dynamically deleted by itself or by another one using the following service:

#include <chorus.h>

int threadDelete(KnCap* actorCap,
                 KnThreadLid thLi);

This call enables a thread to delete another one inside the same actor, when actorCap is set to K_MYACTOR, by knowing the thread identifier of the thread to be deleted. It also enables a thread to delete another one inside another actor (provided they are both running on the same machine), as long as it provides both the actor capability and the target thread identifier. The predefined thread identifier K_MYSELF enables a thread to name itself without knowing its actual thread identifier.

Example 6-2 is a slightly different version of the previous program. The subroutine childCreate() is unchanged, but now the created thread kills itself, instead of going idle forever.


Note -

This does not solve the synchronization problem occurring in the previous example: the main thread still does not know exactly when to terminate the actor.


Refer to the threadDelete(2K) man page.


Example 6-2 Deleting a Thread

(file: progov/thDelete.c)

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

#include <chorus.h>

#define USER_STACK_SIZE (1024 * sizeof(long))

    int
childCreate(KnPc entry)
{
  KnActorPrivilege      actorP;
  KnDefaultStartInfo_f  startInfo;
  char*                 userStack;
  int                   childLid = -1;
  int                   res;

  startInfo.dsType            = K_DEFAULT_START_INFO;
  startInfo.dsSystemStackSize = K_DEFAULT_STACK_SIZE;

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

  if (actorP == K_SUPACTOR) {
    startInfo.dsPrivilege = K_SUPTHREAD;
  } else {
    startInfo.dsPrivilege = K_USERTHREAD;
  }

  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;
  } 

  startInfo.dsEntry = entry;

  res = threadCreate(K_MYACTOR, &childLid, K_ACTIVE, 0, &startInfo);
  if (res != K_OK) {
    printf("Cannot create the thread, error %d\n", res);
    exit(1);
  }

  return childLid;
}

    void
sampleThread()
{
  int myThreadLi;

  myThreadLi = threadSelf();

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

      /* Suicide */
  threadDelete(K_MYACTOR, K_MYSELF);

      /* Should never reach this point! */
}

int main(int argc, char** argv, char**envp)
{
  int        myThreadLi;
  int        newThreadLi;
  int        res;						      
  KnTimeVal  wait;

  newThreadLi = childCreate((KnPc)sampleThread);

  myThreadLi = threadSelf();

      /* Initialize KnTimeVal structure */
  K_MILLI_TO_TIMEVAL(&wait, 10);

      /*
       * Suspend myself for 10 milliseconds to give the newly
       * created thread the opportunity to run before
       * the actor terminates.
       */
  res = threadDelay(&wait);

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

  return 0;
}