ChorusOS 4.0 Introduction

Mutexes

Assume that the two threads need to access one or more global variables in a consistent fashion. A simple example could be that each of the threads needs to add two numbers to a unique global counter. Whatever the scheduling may be, the unique global counter should always reflect the accurate sum of all numbers added by both threads.

This could be done using semaphores. However, the ChorusOS operating system provides mutexes which have been specifically designed and tuned for these types of needs.

A mutex is a binary flag associated with a queue, possibly empty, of waiting threads. The mutex can be locked or free. At initialization, the mutex is set to the free state.

#include <chorus.h>

int mutexInit(KnMutex* mutex);

As for semaphores, the mutex must have been previously allocated by the user. This implies that mutexes may be allocated where convenient for the application, and that there is no limit imposed by the system on the maximum number of mutexes.

Three operations are provided on these mutexes.

The following example shows a small and simple library routine named sampleAdd() which receives two integer arguments and adds them to a global variable one after the other. The code of the previous semaphore example has been modified so that both the main thread and the created thread perform a number of calls to that library. When the job is done, the main thread prints the result and terminates the actor. Refer to the mutexInit(2K) man page.


Example 6-4 Protecting Shared Data Using Mutexes

(file: progov/mutex.c)

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

#define USER_STACK_SIZE (1024 * sizeof(long))			      
								      
KnSem   sampleSem;
KnMutex sampleMutex;
long    grandTotal;

    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
sampleAdd(int a, int b)
{
  int   res;
 
  res = mutexGet(&sampleMutex);

  grandTotal += a;
  grandTotal += b;

  res = mutexRel(&sampleMutex);
}

    void
sampleThread()
{
  int res;
  int i;

  for(i = 0; i < 10; i++) {
    sampleAdd(threadSelf(), i);        /* Why not ??? */
  }

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

int main(int argc, char** argv, char**envp)
{
  int        i;
  int        newThreadLi;
  int        res;

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

  newThreadLi = childCreate((KnPc)sampleThread);

  for(i = 0; i < 20; i++){
    sampleAdd(threadSelf(), i);      /* Why not ??? */
  }

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

  return 0;
}