ChorusOS 5.0 Application Developer's Guide

Chapter 12 Time Management

This chapter deals with the time management services available on ChorusOS systems and demonstrates the use of the ChorusOS native application programming interfaces for managing CPU and real-time.

After implementing the examples provided in this chapter, you will understand how these APIs might be used in an application.

Time Management Services

The ChorusOS operating system offers five time management services:

The configuration of your ChorusOS operating system determines which services are available. Table 12-1 indicates which services are available for a given configuration:

Table 12-1 Time Management Service Availability

Service 

Availability 

tick 

always available 

date 

configured with DATE

timeout 

always available 

timer 

configured with TIMER

virtual time 

configured with VTIMER

Clock Service (tick)

The tick service enables the system to manage the clock, counting only the ticks since the last reboot. No other tick count is available.

A user or supervisor actor can obtain the time elapsed since the last reboot through the following system call:

#include <exec> 
int sysTime(KnTimeVal* time);

This call fills the time data structure, which is built from the following two fields:

The resolution of the value depends on the platform on which the system is running, and may be obtained by a call to:

#include <chorus.h>
int sysTimeGetRes(KnTimeVal* resolution);

The time value returned at the location defined by the resolution parameter represents the smallest possible difference between two distinct values of the system time.

Handling the Clock

The two functions described previously return K_OK if successful and K_EFAULT if the data is outside the actor's address space. The following code example illustrates the use of these two general functions:


Example 12-1 Handling the Clock

#include <stdio.h>
#include <stdlib.h>
#include <chorus.h>
int main( )
{
    KnTimeVal time;
    int result;
    result = sysTime(&time);
    if (result != K_OK) {
        fprintf(stderr, "error on sysTime %s\n", strSysError(result));
        exit(1);
    }
    printf("system time: %d seconds and %d nanoseconds\n",
					time.tmSec, time.tmNSec);
    result = 	sysTimeGetRes(&time);
    if (result != K_OK) {
        fprintf(stderr, "error on sysTime %s\n", strSysError(result));
        exit(1);
    }
    printf("time resolution: %d seconds and %d nanoseconds\n",
					time.tmSec, time.tmNSec);
}

This program produces the following output:


neon time1_u
started aid = 23
system time: 12013 seconds and 240000000 nanoseconds
time resolution: 0 seconds et 10000000 nanoseconds

Date

The date service enables the ChorusOS operating system to maintain the current date (usually expressed in seconds since 01/01/1970). Calls to set and get the time are available in the standard C libraries ctime and localtime, and are not detailed in this document.

Using the Date Service

This section demonstrates how the date service is handled on ChorusOS systems. The following example uses the ctime() function to convert the system date to a standard character string.


Example 12-2 Using the Date Service

#include <stdio.h>
#include <chorus.h>
#include <date/chDate.h>
int result;
KnTimeVal resol, time1, time2;
int main( )
{
    result = univTimeGetRes(&resolution);
    if (result != K_OK)
        printf("error on univTimeGetRes: %s\n", strSysError(result));
    else
        printf("resolution: %d sec, %d nano\n", resol.tmSec, resol.tmNSec);
    result = univTime (&time1);
    if (result != K_OK)
        printf("error on univTime: %s\n", strSysError(result));
    printf("time is: %d seconds, %d nanoseconds\n", time1.tmSec,
            time1.tmNSec);
    printf(" ==> %s\n", ctime(&time1));
    time2.tmSec = 40000000; time2.tmNSec = 0;
    result = univTimeSet(&time1, &time2);
    if (result != K_OK)
        printf("error on univTimeSet: %s\n", strSysError(result));
    result = univTime (&time1);
    if (result != K_OK)
        printf("error on univTime: %s\n", strSysError(result));
    printf("time is: %d seconds, %d nanoseconds\n", time1.tmSec,
            time1.tmNSec);
    printf(" ==> %s\n", ctime(&time1));
}

This program produces the following output:


neon date_s.r
started aid = 2
resolution: 0 sec, 10000000 nano
time is: 947179797 seconds, 590000000 nanoseconds
		Thu Jan 6 17:29:57 2000
time is: 40000000 seconds, 10000000 nanoseconds
		Thu Apr 8 23:06:40 1971

The Timeout Service

The timeout service enables supervisor actors to set up timeouts. A timeout may be described as a callback performed after a given delay has expired. Callbacks are performed using the LAP invocation mechanism.

The TIMEOUT feature provides the traditional one-shot timeout service. At timeout expiration, a caller-provided handler is executed directly at the interrupt level. The execution is generally performed on the interrupt stack (if it exists) with the thread scheduling disabled. The handler execution environment will be restricted accordingly. This feature is restricted to supervisor threads.

In the current version of the ChorusOS operating system, timeouts are based on a regular system-wide clock tick, and timeout granularity is determined by the clock tick.

The timeout API includes the following system calls:

svSysTimeoutCancel()

Cancel a timeout

svSysTimeoutSet()

Request a timeout

svTimeoutGetRes()

Get timeout resolution

Using Time Outs

The following example demonstrates the use of timeout and LAP handlers. The example assumes that the program is run in supervisor mode.


Example 12-3 Using Timeout/LAP Handlers

In this example, the original value of val is 10. The example includes the following steps:


#include <exec/chExec.h>
#include <exec/chIo.h>
#include <stdcIntf.h>
#include <stdio.h>
#include <exec/chTimeout.h>


int              result;
int              val;


KnTimeVal  servTimeout = {0,   50 * K_NPERM}; /* 50 millisec */


KnTimeVal  servDelay =   {0,  500 * K_NPERM}; /* 5000 millisec */

KnTimeout servTimeoutDesc;
KnThSem servThSem;

   /*
    * Timeout handler, used to test 
    */
    void
toHandler(args, opMsg)
    void* args;
    void* opMsg;
{
     val = val * 2;
     threadSemPost(&servThSem);
     sysWrite("timeout handler", strlen("timeout handler"));
}

    void 
timeout() 
{
    void *opMsg;


        if (_stdc_execPrivilege == K_SUPACTOR) {
                KnLapDesc lapDesc;
                result = svLapCreate(&_stdc_execActor, toHandler, 
                                     opMsg, K_LAP_SAMEHOST, &lapDesc);
                if (result != K_EOK) {
                    printf("svLapCreate error\n");
                }

                result = svSysTimeoutSet(&servTimeoutDesc, &servTimeout, 
                                         0, &lapDesc);
                if (result != K_EOK) {
                    printf("svSysTimeoutSet error\n");
                }
                    /* Wait for the timeout handler to complete.
                       should NOT return K_ETIMEOUT */
                result = threadSemWait(&servThSem, &servDelay);
                if (result != K_EOK) {
                    printf("threadSemWait error \n");
                }
                printf("timeout completed\n");
        }
}


main(int argc, char* argv[], char** envp)
{
    val = 10;
    printf("val = %d\n",val);
    threadSemInit(&servThSem);
    timeout();
    printf("val = %d\n",val);
}

Timers

The timer service is an extension of the timeout mechanism, enabling user and supervisor actors to set up call backs in a flexible manner. Both one-shot and periodic timers are provided. timeout notification is achieved through user-provided handler threads which are woken in the application actor.

The timer facility uses the principle of a timer object within the actor. Timer objects may be created, set and deleted dynamically. After being created, the timer objects are addressed by local identifiers within the context of the actor, and are deleted automatically when the actor terminates.

The application creates one or more threads dedicated to timer notification handling by declaring themselves ready to handle these types of events. The relationship between a timer object and a thread (or set of threads) is established trough a threadPool object that is used to block threads waiting for expiration of a timer.

Therefore, the basic mechanism for dealing with timers is:

  1. Allocate and initialize a threadPool object.

  2. Create one thread which will block on the threadPool object.

  3. Create a timer associated with the threadPool object.

  4. Set the timer (effectively arming it).

The second and third steps may be performed in any order. When timer expiration occurs, the dedicated thread is unblocked so that it may now execute any operation that should be performed at timer expiration. For example, the thread may print a warning message, re-arm the timer (unless it is a periodic timer), and block itself again. As is usually the case with the ChorusOS operating system data structures, these threadPool objects must be pre-allocated by the application.

A threadPool object is initialized as follows:

#include <etimer>
int timerThreadPoolInit(KnThreadPool* threadPool);

A timer can then be created as follows:

int timerCreate(KnCap*				actorCap,
						 int					clockType,
						 KnThreadPool*		threadPool,
						 void*				cookie,
						 int*					timerLi);

The previous code fragment creates a timer object in the actor defined by the actorCap parameter. Applications usually use K_MYACTOR. When the timer is armed and reaches expiration, one of the threads blocked on the threadPool object is selected and awakened. This thread is passed the cookie parameter of the timerCreate(2K) call. If successful, timerCreate(2K) returns the local identifier of the created timer in the location defined by the timerLi parameter. At the time of this printing the only clock type supported is K_CLOCK_REALTIME, which corresponds to the time returned by sysTime(2K).

A thread can block itself on a threadPool object through the following call:

#include <etimer>
int timerThreadPoolWait(KnThreadPool* threadPool,
                        void**        cookie,
                        int*          overrun,
                        KnTimeVal*    waitLimit);

Note -

The threadPool object must have been initialized previously.


The timerThreadPoolWait(2K) call blocks the invoking thread until a timer associated with threadPool expires or until the waitLimit condition is reached. Upon timer expiration, the thread returns from this call, and the cookie field has been updated with the value associated with the timer.

The overrun counter is used to indicate to the thread that the timeout notification has been delayed (in this case the overrun value is 1), or that a number of timeout notifications have been lost (in this case the overrun value is always greater than 1).

A timer may be armed with:

#include <etimer>
int timerSet(KnCap*    actorCap,
             int       timerLi,
             int       flag,
             KnITimer* new,
             KnITimer* old);

The timerSet() call arms the timer defined by the first two parameters where timerLi is the timer identifier returned by timerCreate(2K). The timerSet(2K) call enables the specification of the timeout with a relative or an absolute time (using the flag parameter). The timeout can be specified using the new parameter, a structure containing the following fields:

If the old parameter is non-NULL, the time remaining before the timer expires is returned at the location defined by old. If new is non-NULL and the timer has already been set, the current setting is cancelled and replaced with the new one. If the new time specified is 0, the current setting is simply cancelled. If new is set to NULL, the current setting specification is left unchanged.

For more information, refer to the timerThreadPoolInit(2K), timerCreate(2K), timerSet(2K), timerSet(2K), timerThreadPoolWait(2K) man pages.

Using Timers

The following example illustrates the use of timer services for both user and supervisor actors.


Example 12-4 Using Timers

(file: opt/examples/progov/timers.c)
#include <stdio.h>
#include <stdlib.h>
#include <chorus.h>
#include <etimer.h>

KnThreadPool samplePool;
int          periodic;
int          oneShot;
int          periodicLid;
int          oneShotLid;

#define USER_STACK_SIZE (1024 * sizeof(long))			      
								      
KnSem   sampleSem; /* Semaphore allocated as global variable */

    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
timerWait(int myThLi)
{
}

    void
sampleThread()
{
  int      myThLi;
  int      res;
  void*    cookie;
  int      overrun;
  KnITimer periodicTimer;
  KnTimeVal tv;

  myThLi = threadSelf();
  printf("Thread %d started\n", myThLi);

  for(;;) {
      res = timerThreadPoolWait(&samplePool, &cookie, 
            &overrun, K_NOTIMEOUT);
      if (res != K_OK) {					      
          printf("Cannot wait on thread pool, error %d\n", res);      
          exit(1);						      
      }								      
      if (overrun != 0) {
          printf("Thread %d. We were late! overrun set to : %d\n", 
                  myThLi, overrun);
      }
      if (cookie == &periodic) {
         printf("Thread %d. Time is flying away!\n", myThLi);
      } else if (cookie == &oneShot) {
         printf("Thread %d. Isn't it time to go home?\n", myThLi);
         periodicTimer.ITmValue.tmSec   = 0; /* seconds */
         periodicTimer.ITmValue.tmNSec  = 0; /* nanoseconds */
         periodicTimer.ITmReload.tmSec  = 0; /* seconds */
         periodicTimer.ITmReload.tmNSec = 0; /* nanoseconds */
         res = timerSet(K_MYACTOR, periodicLid, 
	       NULL, &periodicTimer, NULL);
         if (res != K_OK) {					      
            printf("Cannot cancel periodic timer, error %d\n", res);  
            exit(1);						      
         }							      
         /* 
          * Periodic timer is cancelled 
          * Get current time,
          * Wait for a short while (3 seconds) and quit
          */
         res = sysTime(&tv);
         if (res != K_OK) {					      
            printf("Cannot get system time, error %d\n", res);	      
            exit(1);						      
         }							      
         printf("Current system time is %d seconds\n", tv.tmSec);
         printf("No more periodic messages should be printed now!\n");
         K_MILLI_TO_TIMEVAL(&tv, 3000);
         (void) threadDelay(&tv);
         /* We are all done ! */
         exit(0);
      } else {
         printf("Spurious timer!\n");
      }
  } /* for() */
}

int main(int argc, char** argv, char** envp) 
{
   int       res;
   KnTimeVal tv;
   int       thLi1;
   int       thLi2;
   KnITimer  periodicTimer;
   KnITimer  oneShotTimer;
	
   res = timerThreadPoolInit(&samplePool);
   if (res != K_OK) {						      
      printf("Cannot initialize thread pool, error %d\n", res);	      
      exit(1);							      
   }								      

   res = timerCreate(K_MYACTOR, K_CLOCK_REALTIME, &samplePool,
			   &periodic, &periodicLid);
   if (res != K_OK) {						      
      printf("Cannot create periodic timer, error %d\n", res);	      
      exit(1);							      
   }								      

   res = timerCreate(K_MYACTOR, K_CLOCK_REALTIME, &samplePool,
			   &oneShot, &oneShotLid);
   if (res != K_OK) {						      
      printf("Cannot create one shot timer, error %d\n", res);	      
      exit(1);							      
   }								      

   thLi1 = childCreate((KnPc)sampleThread);
   thLi2 = childCreate((KnPc)sampleThread);
      
   res = sysTime(&tv);
   if (res != K_OK) {						      
      printf("Cannot get system time, error %d\n", res);	      
      exit(1);							      
   }								      
   printf("Current system time is %d seconds\n", tv.tmSec);

   periodicTimer.ITmValue.tmSec   = 1; /* seconds */
   periodicTimer.ITmValue.tmNSec  = 0; /* nanoseconds */
   periodicTimer.ITmReload.tmSec  = 1; /* seconds */
   periodicTimer.ITmReload.tmNSec = 0; /* nanoseconds */
   res = timerSet(K_MYACTOR, periodicLid, NULL, &periodicTimer, NULL);
   if (res != K_OK) {						      
      printf("Cannot arm periodic timer, error %d\n", res);	      
      exit(1);							      
   }								      
   
   oneShotTimer.ITmValue.tmSec   = tv.tmSec + 30; /* seconds */
   oneShotTimer.ITmValue.tmNSec  = 0; /* nanoseconds */
   oneShotTimer.ITmReload.tmSec  = 0; /* seconds */
   oneShotTimer.ITmReload.tmNSec = 0; /* nanoseconds */

   res = timerSet(K_MYACTOR, oneShotLid, K_TIMER_ABSOLUTE, 
	          &oneShotTimer, NULL);
   if (res != K_OK) {						      
      printf("Cannot arm one shot timer, error %d\n", res);	      
      exit(1);							      
   }								      

   res = threadDelete(K_MYACTOR, K_MYSELF);
   if (res != K_OK) {						      
      printf("Cannot kill myself, error %d\n", res);		      
      exit(1);							      
   }								      

   return 0;
}

The previous example includes the following step: