NASD Programmer's Documentation
Timeouts

While delays can be useful for introducing synchronous latency into code, sometimes it is necessary to receive asynchronous notification that a particular time has arrived. The timeout module provides a mechanism for doing so.

The timeout module is initialized with nasd_timeout_init(), which returns nasd_status_t, and deinitialized with nasd_timeout_shutdown(). The primary interfaces to the timeout module are:

nasd_status_t nasd_timeout_init(void);

void nasd_timeout_shutdown(void);

nasd_status_t nasd_timeout_add(nasd_timeout_handle_t *tmhp,
  nasd_timeout_proc_t callback_proc, void *arg1, void *arg2,
  nasd_timespec_t first, nasd_timespec_t interval,
  nasd_timeout_flags_t flags);

nasd_status_t nasd_timeout_get_status(nasd_timeout_handle_t tmh,
  nasd_timeout_status_t *tsp);

nasd_status_t nasd_timeout_cancel(nasd_timeout_handle_t tmh);

void nasd_timeout_suspend(void);

void nasd_timeout_resume(void);

To register a callback, call nasd_timeout_add(). The first argument is a pointer to a nasd_timeout_handle_t, which is an opaque type which represents this particular timed event. The second argument is a pointer to a function to call when the event occurs. Its signature is: void callback_proc(
  nasd_timeout_handle_t   tm,
  void                   *arg1,
  void                   *arg2);
The next two arguments to nasd_timeout_add() are passed as arg1 and arg2 to callback_proc. (The first argument to callback_proc is the handle of the timeout which generated the callback.) first is the time at which this timer will expire (that is, when callback_proc is called). flags is a bitwise OR of none or more of the following flags:

FlagMeaning
NASD_TIMEOUT_F_ABSOLUTE The value in first should not be taken relative to the current time. That is, if this flag is absent, and a timespec valued at one second is passed, the timer will expire after one second has elapsed. If this flag is set, the timer will expire at the time when nasd_gettime() would return the value of first, or immediately, whichever is later.
NASD_TIMEOUT_F_PERIODIC After expiring at the time indicated by first, it should repeatedly expire every interval thereafter. That is, if first were valued at an absolute time T, and interval were valued at one second, the timer would expire at time T, time T+(1 second), time T+(2 seconds), etc., until the timeout is cancelled or the timeout module is shut down for the last time.
If NASD_TIMEOUT_F_PEROIDIC is not specified in flags then interval is ignored.

The timeout handle returned by nasd_timeout_add() can be used to determine the current state of a timeout by calling nasd_timeout_get_status(). The first argument to this function is this timeout handle. The second argument is a pointer to a nasd_timeout_flags_t word, which is a bitwise OR of none or more of the following:

FlagMeaning
NASD_TIMEOUT_S_KNOWN This is a valid handle.
NASD_TIMEOUT_S_RUNNING Another thread is currently executing the callback_proc for this timeout.
NASD_TIMEOUT_S_CANCELLED nasd_timeout_cancel() has been called on this timeout handle.
NASD_TIMEOUT_S_PERIODIC The NASD_TIMEOUT_F_PERIODIC flag was passed to the call to nasd_timeout_add() which created this handle.

It is completely legal for the callback_proc of a timeout to call nasd_timeout_get_status() on its own timeout handle (which is one reason that the timeout handle is automatically passed to callback_proc as its first argument).

A timeout may be cancelled (removed from the system) by calling nasd_timeout_cancel(), which takes as its sole argument the handle of the timeout to cancel. If the callback_proc for the timeout has not yet begun executing, the timeout will never execute. If it has already begun executing, it will be marked as cancelled, and future calls to nasd_timeout_get_status() will include NASD_TIMEOUT_S_CANCELLED in the resulting flags.

A callback for a timeout function may cancel its own timeout handle. This is useful for periodic timeouts which have decided that they do not need to occur again.

To avoid race conditions cancelling timeouts, users of the timeout module who cancel timeouts are responsible for providing their own synchronization of cancels and cancel checks. This might look like:

nasd_status_t
do_cancel()
{
  nasd_status_t rc;

  NASD_LOCK_MUTEX(some_local_mutex);
  rc = nasd_timeout_cancel(timeout_handle);
  NASD_UNLOCK_MUTEX(some_local_mutex);

  return(rc);
}

void
callback_proc(
  nasd_timeout_handle_t   tm,
  void                   *arg1,
  void                   *arg2);
{
  nasd_timeout_status_t status;
  nasd_status_t rc;

  NASD_LOCK_MUTEX(some_local_mutex);
  rc = nasd_timeout_get_status(timeout_handle, &status);
  if (rc) {
    /* If this were real code, we'd do something graceful here */
    NASD_PANIC();
  }
  if (!(status&NASD_TIMEOUT_S_CANCELLED)) {
    /* perform timeout activity here */
  }
  NASD_UNLOCK_MUTEX(some_local_mutex);
}

There are, of course, other ways to avoid the race condition; this is simply intended as a sample.

To disable all timeouts, call nasd_timeout_suspend(). After this returns, no timeout callback procedures will be dispatched until nasd_timeout_resume() is called. The notion of suspension is reference counted, so if nasd_timeout_suspend() is called N times, and nasd_timeout_resume() is called N-1 times, timeout callback procedures will still not be dispatched until nasd_timeout_resume() is called once more.
<--- ---> ^<br>|<br>|
Delays Shutdown lists NASD Programmer's Documentation