The ChorusOS operating system provides the following time management features:
General interval timing
Virtual timer
Time of day (universal time)
Real-time Clock
Watchdog timer
Benchmark timing
High resolution timing
The interrupt-level timing feature is always available and provides a traditional, one-shot, timeout service. Time-outs and the timeout granularity are based on a system-wide clock tick.
When the timer expires, a caller-provided handler is executed directly at the interrupt level. This is generally on the interrupt stack, if one exists, and with thread scheduling disabled. Therefore, the execution environment is restricted accordingly.
The TIMER feature implements a high-level timer service for both user and supervisor actors. One-shot and periodic timers are provided, with timeout notification via the execution of a user-provided upcall function by a handler thread in the application actor. Handler threads can invoke any microkernel or subsystem system call. This service is implemented using the TIMEOUT feature.
The extended timer facility uses the concept of a timer object in the actor environment. Timers are created and deleted dynamically. Once created, they are addressed by a local identifier in the context of their owning actor, and are deleted automatically when that actor terminates. Timer objects provide a naming mechanism and a locus of control for timing activities. All high-level timer operations, for example, setting, modifying, querying, or canceling pending timeouts, refer to timer objects. Timer objects are also involved in coordination with the threads used to execute application-level notification handlers.
Applications will typically use extended timer functions via a standard application-level library (see "POSIX Timers (POSIX-TIMERS)"). Timer handler threads are created and managed by this library. The library is expected to preallocate stack area for the notification functions, create the thread, and initialize the thread's priority, per-thread data, and all other aspects of its execution context, using standard system calls. The thread then declares itself available for execution of the tier notification (timerThreadPoolWait(2K)) system call to wait for the first or next relevant timeout event. Event arrival will cause the thread to return from the system call, at which point the library code can call the application's handler. The thread pool interface is designed to allow one or a small number of handler threads to service an arbitrary number of timers. An application can thus create a large number of handlers without the expense of a dedicated handler thread for each one.
At most, a single notification will be active for a given timer at any point in time. If no handler thread is available when the timer interval expires, either because the notification function is still executing from a previous expiration or because the handler thread(s) is(are) occupied executing notifications for other timers, an overrun occurs. When a handler thread becomes available (namely, re-executes timerThreadPoolWait()), it will return immediately and the notification function can be invoked immediately. At return from timerThreadPoolWait(), an overrun count is delivered to the thread. An overrun count value pertains to a particular execution of the notification function. The overrun count is defined as the number of timer expirations that have occurred since the preceding invocation of the notifying function without a handler thread being available. Thus for a periodic timer, an overrun count equal to one indicates that the current invocation was delayed, but by less than the period interval.
For details, see the TIMER(5FEA) man page.
The general interval timer (TIMER) API is summarized in the following table:
Function |
Description |
---|---|
timerThreadPoolInit() |
Initialize a thread pool |
timerThreadPoolWait() |
Wait for timer events |
timerCreate() |
Create a timer |
timerDelete() |
Delete a timer |
timerGetRes() |
Get timer resolution |
timerSet() |
Set a timer |
The VTIMER feature is responsible for all functions pertaining to measurement and timing of thread execution. It exports a number of functions that are used typically by higher-level operating system subsystems, such as, UNIX.
VTIMER functions include thread accounting (threadTimes(2K)) and virtual timeouts (svVirtualTimeoutSet(2K) and svVirtualTimeoutCancel(2K)). A virtual timeout handler is entered as soon as the designated thread or threads have consumed the specified amount of execution time. Virtual timeouts can be set either on individual threads, for support of subsystem-level virtual timers (for example, SVR4, setitimer, VIRTUAL, and PROF timers), or on entire actors, for support of process CPU limits.
A virtual timeout handler is entered as soon as one or more designated threads have consumed the specified amount of execution time.
Execution time accounting can be limited to execution in the thread's home actor (internal execution time), or can include cross-actor invocations such as system calls (total execution time).
The svThreadVirtualTimeout() and svThreadActorTimeout() handlers are invoked at thread level and thus can use any API service, including blocking system calls. Timeout events are delivered to application threads, such as threadAbort(), that is, a thread executes a virtual time handler on its own behalf.
For details about virtual time, see the VTIMER(5FEA) man page
The virtual time API is summarized in the following table:
Function |
Description |
---|---|
svActorVirtualTimeoutCancel() |
Cancel an actor virtual timeout |
svActorVirtualTimeoutSet() |
Set an actor virtual timeout |
svThreadVirtualTimeoutCancel() |
Cancel a thread virtual timeout |
svThreadVirtualTimeoutSet() |
Set a thread virtual timeout |
svVirtualTimeoutCancel() |
Cancel a virtual timeout |
svVirtualTimeoutSet() |
Set a virtual timeout |
threadTimes() |
Get thread execution times |
virtualTimeGetRes() |
Get virtual time resolution |
The DATE
feature maintains the time of day
expressed in Universal Time, which is defined as the interval since 1st January
1970. Since the concept of local time is not supported directly by the operating
system, time-zones and local seasonal adjustments must be handled by libraries
outside the microkernel.
For details, see the DATE(5FEA) man page.
The DATE API is summarized in the following table:
Function |
Description |
---|---|
univTime() |
Get time of day |
univTimeAdjust() |
Adjust time of day |
univTimeGetRes() |
Get time of day resolution |
univTimeSet() |
Set time of day |
ChorusOS 5.0 provides time and date management services that comply to Time Zone and Day Light Saving Time behaviors.
The date management utilities and API is summarized in the following table:
Function |
Description |
---|---|
date() |
Print or/and set the date |
settimeoftheday() |
System call to set the time of the day |
gettimeoftheday() |
System call to get the time of the day |
adjtime() |
System call to adjust the time of the day smoothly (used by the Network Time Protocol, NTP) |
ctime() |
Returns time argument as local time in ASCII string |
localtime() |
Returns time argument as local time in a structure |
gmtime() |
Returns time argument without local adjustment |
asctime() |
Returns ASCII time from time structure argument |
mktime() |
Returns time value from time structure argument |
strftime() |
Format printf like time from structure argument |
tzset() |
Set time zone information for time conversion routines |
The RTC
feature indicates whether a real-time clock (RTC) device
is present on the target machine. When this feature is set and an RTC is present on the target, the DATE
feature will retrieve time information from the RTC. If
the RTC
feature is not set, indicating an RTC is not present on the target, the DATE
feature will emulate the RTC in software.
For details, see the RTC(5FEA) man page.
The watchdog timer feature enables a two-step watchdog mechanism on hardware. It consists of a lower-level system layer provided by the driver, that exposes a DDI, and a higher-level layer that hides the DDI and provides an easier API for any user program. The watchdog itself has two steps:
If the watchdog is not patted within a certain delay, an interrupt handler provided by the system is invoked. This interrupt handler attempts to shut down the system and to perform a system dump of the node to collect evidence of the problem.
If the interrupt step gets stuck or lasts too long, the watchdog resets the board, causing it to reboot.
The watchdog is either started by the system at system initialization or possibly by the boot loader. It is expected that a dedicated user-level process will be responsible for patting the watchdog throughout the normal life of the system. A failure in the patting process will lead to the interrupt step of the watchdog mechanism.
To cope gracefully with transitions at initialization time, as well as at system shut-down time, the system is designed to pat the watchdog by itself for a configurable amount of time at system initialization and system shut down. During these periods, where a patting process in user mode might not be possible, the system will play that role implicitly. However, the duration of these initialization and shut-down periods is bound to system configurable values, so it is impossible for initialization to reach the point where the user-level patting process begins without the watchdog interrupt occurring. Similarly, shut down is guaranteed to be bound, or the watchdog interrupt will occur.
Some hardware can support more than one watchdog. The API copes with such situations by associating handles to watchdogs. The WDT feature API is similar to the watchdog API for the Solaris operating environment.
For details on watchdog timer, see the WDT(5FEA) man page.
The watchdog timer API is summarized in the following table:
Function |
Description |
---|---|
wdt_pat() |
Pat (reload) the watchdog timer |
wdt_alloc() |
Allocate a watchdog timer |
wdt_realloc() |
Reallocate a watchdog timer |
wdt_free() |
Disarm and free a watchdog timer |
wdt_get_maxinterval() |
Get the maximum limit (hardware) of a watchdog |
wdt_set_interval() |
Set the interval duration of a watchdog |
wdt_get_interval() |
Get the interval duration of a watchdog |
wdt_arm() |
Arm a watchdog |
wdt_disarm() |
Disarm a watchdog |
wdt_is_armed() |
Check whether a watchdog is armed |
wdt_startup_commit() |
Tells the system the initialiazation phase is over |
wdt_shutdown() |
Tells the system to start patting for shut down |
The wdt_realloc() function enables a process to regain control over a watchdog allocated by a possibly dead process.
The benchmark timing (PERF) feature provides a very precise measurement of short events. It is used primarily for performance benchmarking.
The PERF API closely follows that of the TIMER feature.
For details, see the PERF(5FEA) man page.
The high resolution timer feature provides access to a fine-grained counter source for applications. This counter is used for functions such as fine-grained ordering of events in a node, measurements of short segments of code and fault-detection mechanisms between nodes.
The high resolution timer has a resolution better than or equal to one microsecond, and does not roll over more than once per day.
The high resolution timer API is summarized in the following table:
Function |
Description |
---|---|
hrTimerValue() |
Get the current value of the fine-grained timer in ticks |
hrTimerFrequency() |
Get the frequency of the fine-grained timer in Hertz |
hrTimerPeriod() |
Get the difference between the minimum and the maximum of the possible values of the fine-grained timer in ticks |