Newsgroups: comp.sources.unix From: panos@anchor.cs.colorado.edu (Panos Tsirigotis) Subject: v27i122: clc - C Libraries Collection, Part16/20 References: <1.754527080.23891@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: panos@anchor.cs.colorado.edu (Panos Tsirigotis) Posting-Number: Volume 27, Issue 122 Archive-Name: clc/part16 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'libs/src/dict/dict.3' <<'END_OF_FILE' X.\"(c) Copyright 1993 by Panagiotis Tsirigotis X.\"All rights reserved. The file named COPYRIGHT specifies the terms X.\"and conditions for redistribution. X.\" X.\" $Id: dict.3,v 3.4 93/11/23 20:07:28 panos Exp $ X.TH DICT 3X "23 April 1993" X.SH NAME Xdictionary management functions X.SH SYNOPSIS X.LP X.nf X.ft B X#include "dict.h" X#include ".h" X.LP X.ft B Xdict_h _create( oo_compare, ko_compare, flags, errnop ) Xint (*oo_compare)( dict_obj, dict_obj ) ; Xint (*ko_compare)( dict_key, dict_obj ) ; Xint flags ; Xint *errnop ; X.LP X.ft B Xvoid _destroy( handle ) Xdict_h handle ; X.LP X.ft B Xint _insert( handle, object ) Xdict_h handle ; Xdict_obj object ; X.LP X.ft B Xint _insert_uniq( handle, object, objectp ) Xdict_h handle ; Xdict_obj object ; Xdict_obj *objectp ; X.LP X.ft B Xdict_obj _search( handle, key ) Xdict_h handle ; Xdict_key key ; X.LP X.ft B Xint _delete( handle, object ) Xdict_h handle ; Xdict_obj object ; X.LP X.ft B Xdict_obj _minimum( handle ) Xdict_h handle ; X.LP X.ft B Xdict_obj _maximum( handle ) Xdict_h handle ; X.LP X.ft B Xdict_obj _successor( handle, object ) Xdict_h handle ; Xdict_obj object ; X.LP X.ft B Xdict_obj _predecessor( handle, object ) Xdict_h handle ; Xdict_obj object ; X.LP X.ft B Xvoid _iterate( handle, direction ) Xdict_h handle ; Xenum dict_direction direction ; X.LP X.ft B Xdict_obj _nextobj( handle ) Xdict_h handle ; X.LP X.ft B Xextern int dict_errno ; X.SH DESCRIPTION XThis is a framework for libraries that solve the dictionary problem: Xinsertion, location, deletion of objects from a set. XEach library uses a unique prefix in place of X.I "_" Xthat reflects the way it works. XFor example, the X.I "binary search tree" Xlibrary uses the X.I bst_ Xprefix. XA dictionary library provides only structure for the objects stored in it. XIt does not provide storage space. XTherefore, the X.I dict_obj Xand X.I dict_key Xtypes are pointer types (the exact pointer type is implementation-dependent). XThere is no copying of keys. The key is part of the object. X.LP XSome dictionary libraries keep the inserted objects ordered. Others Xmay not support ordering. Others still may support either ordered or Xunordered objects. XWhen a library instance is created, the caller Xcan select between ordered and unordered objects depending on the requirements Xof the application. XA request for ordering will be rejected if the library does not support it, Xbut a request for lack of ordering is usually treated as a hint, and it may Xnot be rejected. X.LP X.B _create Xcreates a library instance and returns a handle to it. XObject-object comparisons will be required when key uniqueness is requested, Xor if the library needs to impose an order on the inserted objects. XSuch comparisons are done using the X.I oo_compare Xfunction. XThis function will be called with two arguments of type X.I dict_obj, Xand it should return an integer less-than/equal-to/greater-than 0 Xif the first argument is less-than/equal-to/greater-than the second Xargument (therefore, it is up to the programmer to define the object ordering). XIf the library supports the X.B DICT_UNORDERED Xflag (which requests that the objects not be ordered), then Xproviding an X.I oo_compare Xfunction is optional. X.LP XThe X.I ko_compare Xfunction (also specified at the time of library instance creation) is used by X.B _search() Xto locate objects with the specified key value. XIt will be called with 2 arguments, the first of type X.I dict_key, Xand the second of type X.I dict_obj, Xand it should return an integer less-than/equal-to/greater-than 0 Xif the specified key (first argument) is less-than/equal-to/greater-than Xthe key of the object (second argument). Specifying this function is Xoptional, but you should note that use of X.B _search() Xwhen this function has not been specified will cause abnormal program Xtermination. X.LP XThe X.I flags Xargument is formed by ORing one or more of the following constants: X.RS X.TP X.SB DICT_RETURN_ERROR XIf a call encounters an error condition, it will return a value that Xindicates that an error occurred. The default is to terminate the Xprogram with an appropriate error message. The errors that are Xhandled this way are usually non-recoverable (for example, lack of Xmemory), or they are programmer errors. XErrors like searching for an object that is not in the library instance, Xor trying to insert an object with a non-unique key value, Xdo not terminate the program. X.TP X.SB DICT_UNIQUE_KEYS XObjects with equal keys are allowed unless this flag is specified. XIf objects with equal keys are allowed, it is guaranteed that a X.B _search() Xoperation will locate one of them, and subsequent X.B _successor() Xoperations will return the rest of them. X.TP X.SB DICT_UNORDERED XRequests that inserted objects are not ordered. This request may be Xrejected if the library requires object ordering. X.TP X.SB DICT_ORDERED XRequests that inserted objects are ordered. This request may be rejected Xif the library does not support object ordering. X.RE X.LP XSome dictionary library implementations support additional flags. XSuch flags will be quietly ignored by implementations that don't support them. XThe constant X.B DICT_NOFLAGS Xcan be used to specify no flags. XThe X.I errnop Xargument specifies a location to place error codes when a call fails. XIf it is specified as X.I "(int *)0," Xthe integer variable X.I dict_errno Xwill be used for this purpose. X.LP X.B _destroy() Xdestroys the library instance identified by the X.I handle. X.LP X.B _insert() Xand X.B _insert_uniq() Xare used for object insertions, with the latter requiring that the Xnew object be unique (in terms of its key). XIf the X.I objectp Xargument of X.B _insert_uniq() Xis not X.SM NULL Xthen if the insertion is successful, it will point to X.I object. XIf the insertion is not successful because there is already an Xobject with an equal key value, then X.I objectp Xwill point to that object. X.LP X.B _delete() Xdeletes an object from a library instance. XIt is not a programmer error if the object does not exist in the library Xinstance (i.e. the program will not be terminated if the X.SB DICT_RETURN_ERROR Xflag is not specified; an error code will always be returned instead). X.LP X.B _search() Xlocates objects with a key equal to the specified key. X.LP XThe action of the X.B _minimum(), X.B _maximum(), X.B _successor(), Xand X.B _predecessor() Xoperations depends on whether the library orders the objects stored Xin it. If it does, then these operations have the meaning denoted by Xtheir names (although it should be noted that the order is really Xdefined by the X.I oo_compare Xfunction and may not be intuitive). XIf the objects are stored in any order, then the meaning of these Xoperations is undefined. However, Xit is guaranteed that by starting Xat the object identified by X.B "_minimum()" Xor X.B "_maximum()," Xand iterating with X.B "_successor()" Xor X.B "_predecessor()" Xrespectively, Xall objects stored in the library instance will be enumerated. X.LP X.B _successor() Xreturns the object that is the successor of the specified X.I object. XThe specified object must exist in the library instance X(non-existence is considered a programmer error). X.LP X.B _predecessor() Xreturns the object that is the predecessor of the specified X.I object. XThe specified object must exist in the library instance X(non-existence is considered a programmer error). X.LP X.B "_iterate()" Xprepares the library instance identified by X.I handle Xfor an iteration. XAssuming a library that orders objects according to non-decreasing key value, Xif X.I direction Xis X.I DICT_FROM_START Xthen the objects will be iterated according to non-decreasing key value, Xwhile if X.I direction Xis X.I DICT_FROM_END Xthen the objects will be iterated according to non-increasing key value. XIf the library does not provide any ordering, then the X.I direction Xargument is ignored. X.LP X.B "_nextobj()" Xreturns the next object. XThe reason for providing X.B "_iterate()" Xand X.B "_nextobj()" Xis that they are more convenient to use when it is desirable Xto optionally delete the object returned from X.B "_nextobj()" Xand continue iterating. X.SH "RETURN VALUES" X.LP XFunctions returning handles or objects, return X.SM NULL Xif they fail. X.LP XFunctions returning \fIint\fRs, return X.B DICT_OK Xon success, and X.B DICT_ERR Xon failure. XWhen a call fails, it sets the error variable to an appropriate value. XThe error variable is either X.I dict_errno, Xor is the one specified in the call to X.B _create() Xwhich created the given library instance. X.LP X.B _create() Xreturns a library instance handle if it succeeds, or X.SM NULL Xif it fails. X.LP X.B _insert() Xreturns X.B DICT_OK Xif it succeeds, or X.B DICT_ERR Xif it fails. X.LP X.B _insert_uniq() Xreturns X.B DICT_OK Xif it succeeds, or X.B DICT_ERR Xif it fails. X.LP X.B _delete() Xreturns X.B DICT_OK Xif it succeeds, or X.B DICT_ERR Xif it fails. X.LP X.B _search() Xreturns an object if it succeeds, or X.SM NULL Xif it fails (the error variable is not set in this case as Xthere is only one explanation for the failure). X.LP X.B _minimum() Xreturns an object, or X.SM NULL Xif there are no objects in the particular library instance. X.LP X.B _maximum() Xreturns an object, or X.SM NULL Xif there are no objects in the particular library instance. X.LP X.B _successor() X.B "(_predecessor())" Xreturns an object, or X.SM NULL Xif the specified object has no successor (predecessor), Xor when the specified object does not exist (assuming the X.SB DICT_RETURN_ERROR Xflag was used when the specific library instance was created). XIn order to discriminate between these two cases, in the former case Xthe error variable X(\fIdict_errno\fP or the one specified when the Xspecific library instance was created) Xwill be set to X.SB DICT_ENOERROR, Xand in the latter case it will contain an error code. X.LP X.B _nextobj() Xreturns an object, or X.SM NULL Xif there are no more objects. X.SH ERRORS X.LP XThe following error codes are placed in X.I dict_errno Xor in the user-specified error variable. XError codes marked with "*" cause program termination if the X.B DICT_RETURN_ERROR Xflag is not used. X.IP DICT_ENOERROR 20 XNo error. X.IP "DICT_ENOMEM *" XOperation failed because of lack of memory. X.IP DICT_ENOTFOUND XObject not found. X.IP "DICT_ENOOOCOMP *" XObject-to-object comparator function is missing. X.\" X.\" .IP "DICT_ENOKOCOMP *" X.\" Key-to-object comparator function is missing. X.\" X.IP "DICT_ENULLOBJECT *" XObject is X.SM NULL. X.IP DICT_EEXISTS XObject with equal key exists. X.IP "DICT_EBADOBJECT *" XThe object used in a X.I "_successor" Xor X.I "_predecessor" Xoperation does not exist. X.IP "DICT_ENOHVFUNC *" XThe function to convert a key or object to a hash value is missing. X.IP "DICT_EBADORDER *" XBoth the X.SM DICT_ORDERED Xand X.SM DICT_UNORDERED Xflags were specified. X.IP "DICT_EORDER" XThe specified order flag is not supported by the particular library Ximplementation. X.SH EXAMPLE XThe following code fragment reads words from standard input and places them Xin a set making sure that the set contains no duplicates. At the Xend-of-file indication, all the words in the set are listed in Xalphanumeric order. A balanced binary search tree is used to maintain Xthe set. X.RS X.sp 1 X.ft B X.nf X#include "bst.h" X.sp 1 Xdict_h word_set ; Xchar buf[ 80 ] ; Xchar *word ; Xint strcmp() ; X.sp 1 Xword_set = bst_create( strcmp, strcmp, X.RS XDICT_UNIQUE_KEYS + DICT_BALANCED_TREE, (int *)0 ) ; X.RE Xwhile ( gets( buf ) ) X{ X.RS X/* X * We expect one word per line X */ Xword = malloc( strlen( buf ) + 1 ) ; X(void) strcpy( word, buf ) ; Xif ( bst_insert( word_set, (dict_obj) word ) == DICT_ERR ) X.RS Xfree( word ) ; X.RE X.RE X} Xfor ( word = (char *) bst_minimum( word_set ) ; word ; X.RS X.RS Xword = (char *) bst_successor( word_set, word ) ) X.RE X.RE X.RS Xprintf( "%s\\n", word ) ; X.RE END_OF_FILE if test 11843 -ne `wc -c <'libs/src/dict/dict.3'`; then echo shar: \"'libs/src/dict/dict.3'\" unpacked with wrong size! fi # end of 'libs/src/dict/dict.3' fi if test -f 'libs/src/timer/ostimer.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libs/src/timer/ostimer.c'\" else echo shar: Extracting \"'libs/src/timer/ostimer.c'\" \(11399 characters\) sed "s/^X//" >'libs/src/timer/ostimer.c' <<'END_OF_FILE' X/* X * (c) Copyright 1993 by Panagiotis Tsirigotis X * All rights reserved. The file named COPYRIGHT specifies the terms X * and conditions for redistribution. X */ X Xstatic char RCSid[] = "$Id: ostimer.c,v 5.1 93/11/26 12:15:00 panos Exp $" ; X X#include X#include X Xextern int errno ; X X#include "timemacros.h" X#include "defs.h" X#include "impl.h" X#include "ostimer.h" X X#ifdef DEBUG X#define DEBUG_REPORT_ERRORS X#endif X X#define OSTIMER_SET( func, otp, itv ) \ X if ( setitimer( (otp)->ost_systype, &(itv), ITIMERVAL_NULL ) == -1 ) \ X report( "TIMER %s: setitimer failed. errno = %d\n", func, errno ) X X#define SET_OSTIMER( otp, itv, func ) \ X if ( TV_ISZERO( (itv).it_value ) ) \ X report( "TIMER %s: zero interval\n", func ) ; \ X else \ X { \ X OSTIMER_SET( func, otp, itv ) ; \ X } X X X/* ARGSUSED */ XPRIVATE void report( fmt, va_alist ) X char *fmt ; X va_dcl X{ X#ifdef DEBUG_REPORT_ERRORS X va_list ap ; X X va_start( ap ) ; X (void) vfprintf( stderr, fmt, ap ) ; X#ifdef DEBUG X abort() ; X _exit( 1 ) ; X#endif X#endif /* DEBUG_REPORT_ERRORS */ X} X X X/* X * Initialize the fields of struct timer that are used by the ostimer code X */ Xint __ostimer_newtimer( tp, type ) X timer_s *tp ; X enum timer_types type ; X{ X ostimer_s *otp = __ostimer_init( tp, type ) ; X X if ( otp == OSTIMER_NULL ) X return( TIMER_ERR ) ; X X tp->t_ostimer = otp ; X TV_ZERO( tp->t_expiration ) ; X TV_ZERO( tp->t_interval ) ; X tp->t_count = 0 ; X return( TIMER_OK ) ; X} X X X X X/* X * The following variables are used to handle the following scenario: X * X * 1. INTERRUPT happens ==> ostimer_interrupt called X * 2. Timers A and B expire. X * 3. The function associated with A is invoked and running X * 4. INTERRUPT happens ==> ostimer_interrupt called X * 5. Timer C expires. X * 6. Function of timer C invoked and returns with a jmp_buf. X * X * If we longjmp to the jmp_buf returned by the function of timer C the X * function of timer B will never be called and the function of timer A X * will never finish. X * What we do instead is have ostimer_interrupt check the call_level X * and if greater than 1, then just save the jmp_buf returned by the X * function of timer C (only if there is no other ret_env) and simply return. X * X * Notice that there can be only one ret_env (since there is only 1 control X * flow). X * X * XXX: this complexity stems from the fact that we allow interrupts while X * the timer functions are running. Is there a good reason for this ? X * (probably because we have to allow interrupts of other timer types). X */ Xstatic int call_level ; Xstatic int have_env ; Xstatic jmp_buf ret_env ; X X#define MAX_EXPIRED 20 X X/* X * ostimer_interrupt invokes the functions of the timers that expired. X * Since we don't know in advance how many timers expired, we follow a X * two-step procedure: X * X * Step 1: collect all expired timers X * Step 2: invoke timer actions X * X * The expired timers are collected in an array stored on the stack. X * If the array overflows, we arrange for another timer interrupt as X * soon as possible to service remaining timers. The reason we don't X * allocate the array using malloc is that malloc is not guaranteed X * to be reentrant and tracking timing-related dynamic memory allocation X * problems is guaranteed to be a nightmare. X * X * Notice that *all* timer interrupts are blocked during step 1. X */ Xvoid __ostimer_interrupt( otp ) X register ostimer_s *otp ; X{ X struct timeval current_time ; X register timer_s *tp ; X timer_s *expired_timers[ MAX_EXPIRED ] ; X unsigned n_expired = 0 ; X int i ; X X if ( timer_pq_head( otp->ost_timerq.tq_handle ) == TIMER_NULL ) X return ; X X call_level++ ; X X (*otp->ost_get_current_time)( ¤t_time ) ; X X /* X * Get all timers that expired X */ X for ( ;; ) X { X tp = timer_pq_head( otp->ost_timerq.tq_handle ) ; X X if ( tp == TIMER_NULL || TV_GT( tp->t_expiration, current_time ) || X n_expired == MAX_EXPIRED ) X break ; X X tp = timer_pq_extract_head( otp->ost_timerq.tq_handle ) ; X if ( tp->t_state == TICKING ) X { X tp->t_state = INACTIVE ; X X tp->t_count++ ; X if ( tp->t_blocked ) X { X if ( tp->t_act == IDLE ) X tp->t_act = PENDING ; X else if ( tp->t_act == INVOKED ) X tp->t_act = SCHEDULED ; X } X else /* not blocked */ X { X if ( tp->t_act == IDLE || tp->t_act == INVOKED ) X { X tp->t_act = SCHEDULED ; X expired_timers[ n_expired++ ] = tp ; X } X } X } X } X X /* X * Check which timers have regular expiration intervals X */ X for ( i = 0 ; i < n_expired ; i++ ) X { X tp = expired_timers[ i ] ; X X if ( ! TV_ISZERO( tp->t_interval ) ) X { X tp->t_state = TICKING ; X TV_ADD( tp->t_expiration, tp->t_expiration, tp->t_interval ) ; X /* X * We shouldn't have an insertion problem since we just extracted X * the timers from the queue (i.e. there should be enough room) X */ X (void) timer_pq_insert( otp->ost_timerq.tq_handle, tp ) ; X } X } X X tp = timer_pq_head( otp->ost_timerq.tq_handle ) ; X X if ( tp != TIMER_NULL ) X { X struct itimerval itv ; X X TV_ZERO( itv.it_interval ) ; X /* X * Check if we had too many expired timers X */ X if ( TV_LE( tp->t_expiration, current_time ) ) X { X itv.it_value.tv_sec = 0 ; X itv.it_value.tv_usec = 1 ; /* schedule an interrupt ASAP */ X /* XXX: this trick will result in another call to X * ostimer_interrupt. So why don't we just call it X * recursively, instead of taking another timer interrupt ? X */ X } X else X TV_SUB( itv.it_value, tp->t_expiration, current_time ) ; X X SET_OSTIMER( otp, itv, "ostimer_interrupt" ) ; X } X X#ifdef DEBUG_MSGS X printf( "\t\t%d timers expired\n", n_expired ) ; X#endif X X /* X * Invoke the functions of all expired timers X */ X for ( i = 0 ; i < n_expired ; i++ ) X { X tp = expired_timers[ i ] ; X if ( __timer_invoke( tp ) != DESTROYED && X ! have_env && ( tp->t_action.ta_flags & TIMER_LONGJMP ) ) X { X (void) memcpy( (char *)ret_env, X (char *)tp->t_action.ta_env, sizeof( jmp_buf ) ) ; X have_env = TRUE ; X } X } X X call_level-- ; X X /* X * If this is not a recursive call, and there is a jmp_buf, use it. X */ X if ( call_level == 0 && have_env ) X { X have_env = FALSE ; X longjmp( ret_env, 1 ) ; X } X} X X X/* X * Carry out the timer action. X * If the call level is 0 X * AND there is not already an environment to longjmp to X * AND this timer has such an environment X * then X * longjmp to that environment X * X * Notice that all timer interrupts are blocked while this function is running. X * Therefore, none of the global variables checked can change. X */ XPRIVATE void invoke_protocol( tp ) X timer_s *tp ; X{ X if ( __timer_invoke( tp ) != DESTROYED && X call_level == 0 && ! have_env && X ( tp->t_action.ta_flags & TIMER_LONGJMP ) ) X longjmp( tp->t_action.ta_env, 1 ) ; X} X X X/* X * Add a timer to the specified OS timer's queue X * We assume that the timer already has a valid state and action X * associated with it. X * We also assume that no operations (block etc) are applied to the X * timer while this function is running. X */ Xint __ostimer_add( otp, tp, itvp, time_type ) X ostimer_s *otp ; X register timer_s *tp ; X struct itimerval *itvp ; X enum timer_timetypes time_type ; X{ X struct timeval current_time ; X int expired ; X X /* X * While this function (__ostimer_add) is running, this will be our X * notion of the current time. X * In reality, there may be some time skew as this function X * is running, possibly because of swapping. X */ X (*otp->ost_get_current_time)( ¤t_time ) ; X X /* X * Since we use absolute time for our calculations, X * convert the user-specified time to that, if necessary X * X * Also check if the timer has already expired. There are 2 possibilities: X * X * 1. a zero interval for TIMER_RELATIVE X * 2. a time before current time for TIMER_ABSOLUTE X * X * Note that we always calculate t_expiration in case the user has X * specified an it_interval. X */ X X if ( time_type == TIMER_RELATIVE ) X { X /* X * timer_start has verified that it_value is not negative X */ X TV_ADD( tp->t_expiration, current_time, itvp->it_value ) ; X expired = TV_ISZERO( itvp->it_value ) ; X } X else X { X tp->t_expiration = itvp->it_value ; X expired = TV_LE( tp->t_expiration, current_time ) ; X } X X tp->t_interval = itvp->it_interval ; X X if ( expired ) X { X tp->t_count++ ; X tp->t_act = SCHEDULED ; X X if ( TV_ISZERO( tp->t_interval ) ) X { X invoke_protocol( tp ) ; X return( TIMER_OK ) ; X } X X /* X * Keep expiring the timer until it exceeds the current time X */ X time_type = TIMER_ABSOLUTE ; X for ( ;; ) X { X TV_ADD( tp->t_expiration, tp->t_expiration, tp->t_interval ) ; X expired = TV_LE( tp->t_expiration, current_time ) ; X if ( ! expired ) X break ; X tp->t_act = SCHEDULED ; X tp->t_count++ ; X } X invoke_protocol( tp ) ; X } X X if ( timer_pq_insert( otp->ost_timerq.tq_handle, tp ) == PQ_ERR ) X HANDLE_ERROR( tp->t_flags, TIMER_ERR, tp->t_errnop, TIMER_ECANTINSERT, X "TIMER __ostimer_add: can't insert timer in priority queue\n" ) ; X X tp->t_state = TICKING ; X X /* X * Now check if we will need to set the timer again X */ X if ( tp == timer_pq_head( otp->ost_timerq.tq_handle ) ) X { X /* X * Check if the user specified relative time. X * If so, use the value given (for better accuracy), otherwise compute X * a new value. X * It is possible that by now the timer that was at the head X * of the queue expired and a signal is pending. This can happen X * if we are swapped out. The signal handling function will X * obviously expire both the new timer and the old one, so our X * setting a new timer value may cause a signal at a later time X * when the queue is empty. The signal handling function should X * be able to deal with an empty queue. X */ X struct itimerval itv ; X X TV_ZERO( itv.it_interval ) ; X if ( time_type == TIMER_RELATIVE ) X itv.it_value = itvp->it_value ; X else X TV_SUB( itv.it_value, tp->t_expiration, current_time ) ; X SET_OSTIMER( otp, itv, "__ostimer_add" ) ; X } X X return( TIMER_OK ) ; X} X X X/* X * Remove the specified timer from the OS timer's queue X * Timer interrupts should be blocked. X */ Xvoid __ostimer_remove( otp, tp ) X ostimer_s *otp ; X timer_s *tp ; X{ X struct itimerval itv ; X struct timeval current_time ; X timer_s *new_head_timer, *old_head_timer ; X X old_head_timer = timer_pq_head( otp->ost_timerq.tq_handle ) ; X timer_pq_delete( otp->ost_timerq.tq_handle, tp ) ; X new_head_timer = timer_pq_head( otp->ost_timerq.tq_handle ) ; X X if ( old_head_timer != new_head_timer ) X { X int do_setitimer ; X X if ( new_head_timer != TIMER_NULL ) X { X (*otp->ost_get_current_time)( ¤t_time ) ; X X /* X * If the head_timer is less than or equal to the current time, X * the interrupt must be pending, so we leave the OS timer running. X * Otherwise, we restart the OS timer. X */ X if ( TV_LE( current_time, new_head_timer->t_expiration ) ) X do_setitimer = FALSE ; X else X { X do_setitimer = TRUE ; X TV_SUB( itv.it_value, new_head_timer->t_expiration, current_time ) ; X } X } X else /* queue is empty */ X { X do_setitimer = TRUE ; X TV_ZERO( itv.it_value ) ; X } X X if ( do_setitimer ) X { X TV_ZERO( itv.it_interval ) ; X OSTIMER_SET( "__ostimer_remove", otp, itv ) ; X } X } X} X END_OF_FILE if test 11399 -ne `wc -c <'libs/src/timer/ostimer.c'`; then echo shar: \"'libs/src/timer/ostimer.c'\" unpacked with wrong size! fi # end of 'libs/src/timer/ostimer.c' fi echo shar: End of archive 16 \(of 20\). cp /dev/null ark16isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 20 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0