cad63b4a3a0599843af4005797c5d6644852b38f
[openafs.git] / src / lwp / timer.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*******************************************************************\
11 *                                                                   *
12 *                                                                   *
13 *       Information Technology Center                               *
14 *       Carnegie-Mellon University                                  *
15 *                                                                   *
16 *                                                                   *
17 *                                                                   *
18 \*******************************************************************/
19
20
21 #include <afsconfig.h>
22 #include <afs/param.h>
23
24 #include <roken.h>
25
26 #define _TIMER_IMPL_
27 #include "timer.h"
28 #include "lwp.h"
29
30
31
32
33 typedef unsigned char bool;
34 #define FALSE   0
35 #define TRUE    1
36
37
38 #define expiration TotalTime
39
40 #define new_elem()      ((struct TM_Elem *) malloc(sizeof(struct TM_Elem)))
41
42 #define MILLION 1000000
43
44 static int globalInitDone = 0;
45
46 /* t1 = t2 - t3 */
47 static void
48 subtract(struct timeval *t1, struct timeval *t2,
49          struct timeval *t3)
50 {
51     int sec2, usec2, sec3, usec3;
52
53     sec2 = t2->tv_sec;
54     usec2 = t2->tv_usec;
55     sec3 = t3->tv_sec;
56     usec3 = t3->tv_usec;
57
58     /* Take care of the probably non-existent case where the
59      * usec field has more than 1 second in it. */
60
61     while (usec3 > usec2) {
62         usec2 += MILLION;
63         sec2--;
64     }
65
66     /* Check for a negative time and use zero for the answer,
67      * since the tv_sec field is unsigned */
68
69     if (sec3 > sec2) {
70         t1->tv_usec = 0;
71         t1->tv_sec = (afs_uint32) 0;
72     } else {
73         t1->tv_usec = usec2 - usec3;
74         t1->tv_sec = sec2 - sec3;
75     }
76 }
77
78 /* t1 += t2; */
79
80 static void
81 add(struct timeval *t1, struct timeval *t2)
82 {
83     t1->tv_usec += t2->tv_usec;
84     t1->tv_sec += t2->tv_sec;
85     if (t1->tv_usec >= MILLION) {
86         t1->tv_sec++;
87         t1->tv_usec -= MILLION;
88     }
89 }
90
91 /* t1 == t2 */
92
93 int
94 TM_eql(struct timeval *t1, struct timeval *t2)
95 {
96     return (t1->tv_usec == t2->tv_usec) && (t1->tv_sec == t2->tv_sec);
97 }
98
99 /* t1 >= t2 */
100
101 /*
102 obsolete, commentless procedure, all done by hand expansion now.
103 static bool geq(struct timeval *t1, struct timeval *t2)
104 {
105     return (t1->tv_sec > t2->tv_sec) ||
106            (t1->tv_sec == t2->tv_sec && t1->tv_usec >= t2->tv_usec);
107 }
108 */
109
110 static bool
111 blocking(struct TM_Elem *t)
112 {
113     return (t->TotalTime.tv_sec < 0 || t->TotalTime.tv_usec < 0);
114 }
115
116
117
118 /*
119     Initializes a list -- returns -1 if failure, else 0.
120 */
121
122 int
123 TM_Init(struct TM_Elem **list)
124 {
125     if (!globalInitDone) {
126         FT_Init(0, 0);
127         globalInitDone = 1;
128     }
129     *list = new_elem();
130     if (*list == NULL)
131         return -1;
132     else {
133         (*list)->Next = *list;
134         (*list)->Prev = *list;
135         (*list)->TotalTime.tv_sec = 0;
136         (*list)->TotalTime.tv_usec = 0;
137         (*list)->TimeLeft.tv_sec = 0;
138         (*list)->TimeLeft.tv_usec = 0;
139         (*list)->BackPointer = NULL;
140
141         return 0;
142     }
143 }
144
145 int
146 TM_Final(struct TM_Elem **list)
147 {
148     if (list == NULL || *list == NULL)
149         return -1;
150     else {
151         free(*list);
152         *list = NULL;
153         return 0;
154     }
155 }
156
157 /*
158     Inserts elem into the timer list pointed to by *tlistPtr.
159 */
160
161 void
162 TM_Insert(struct TM_Elem *tlistPtr, struct TM_Elem *elem)
163 {
164     struct TM_Elem *next;
165
166     /* TimeLeft must be set for function IOMGR with infinite timeouts */
167     elem->TimeLeft = elem->TotalTime;
168
169     /* Special case -- infinite timeout */
170     if (blocking(elem)) {
171         openafs_insque(elem, tlistPtr->Prev);
172         return;
173     }
174
175     /* Finite timeout, set expiration time */
176     FT_AGetTimeOfDay(&elem->expiration, 0);
177     add(&elem->expiration, &elem->TimeLeft);
178     next = NULL;
179     FOR_ALL_ELTS(p, tlistPtr, {
180                  if (blocking(p)
181                      || !(elem->TimeLeft.tv_sec > p->TimeLeft.tv_sec
182                           || (elem->TimeLeft.tv_sec == p->TimeLeft.tv_sec
183                               && elem->TimeLeft.tv_usec >=
184                               p->TimeLeft.tv_usec))
185                  ) {
186                  next = p;      /* Save ptr to element that will be after this one */
187                  break;}
188                  }
189     )
190
191         if (next == NULL)
192             next = tlistPtr;
193     openafs_insque(elem, next->Prev);
194 }
195
196 /*
197     Walks through the specified list and updates the TimeLeft fields in it.
198     Returns number of expired elements in the list.
199 */
200
201 int
202 TM_Rescan(struct TM_Elem *tlist)        /* head pointer of timer list */
203 {
204     struct timeval time;
205     int expired;
206
207     FT_AGetTimeOfDay(&time, 0);
208     expired = 0;
209     FOR_ALL_ELTS(e, tlist, {
210                  if (!blocking(e)) {
211                  subtract(&e->TimeLeft, &e->expiration, &time);
212                  if (0 > e->TimeLeft.tv_sec
213                      || (0 == e->TimeLeft.tv_sec && 0 >= e->TimeLeft.tv_usec))
214                  expired++;}
215                  }
216     )
217         return expired;
218 }
219
220 /*
221     RETURNS POINTER TO earliest expired entry from tlist.
222     Returns 0 if no expired entries are present.
223 */
224
225 struct TM_Elem *
226 TM_GetExpired(struct TM_Elem *tlist)    /* head pointer of timer list */
227 {
228     FOR_ALL_ELTS(e, tlist, {
229                  if (!blocking(e)
230                      && (0 > e->TimeLeft.tv_sec
231                          || (0 == e->TimeLeft.tv_sec
232                              && 0 >= e->TimeLeft.tv_usec)))
233                  return e;}
234     )
235         return NULL;
236 }
237
238 /*
239     Returns a pointer to the earliest unexpired element in tlist.
240     Its TimeLeft field will specify how much time is left.
241     Returns 0 if tlist is empty or if there are no unexpired elements.
242 */
243
244 struct TM_Elem *
245 TM_GetEarliest(struct TM_Elem *tlist)
246 {
247     struct TM_Elem *e;
248
249     e = tlist->Next;
250     return (e == tlist ? NULL : e);
251 }
252
253 /* This used to be in hputils.c, but it's only use is in the LWP package. */
254 /*
255  * Emulate the vax instructions for queue insertion and deletion, somewhat.
256  * A std_queue structure is defined here and used by these routines.  These
257  * routines use caddr_ts so they can operate on any structure.  The std_queue
258  * structure is used rather than proc structures so that when the proc struct
259  * changes only process management code breaks.  The ideal solution would be
260  * to define a std_queue as a global type which is part of all the structures
261  * which are manipulated by these routines.  This would involve considerable
262  * effort...
263  */
264
265 void
266 openafs_insque(struct TM_Elem *elementp, struct TM_Elem *quep)
267 {
268     elementp->Next = quep->Next;
269     elementp->Prev = quep;
270
271     quep->Next->Prev = elementp;
272     quep->Next = elementp;
273 }
274
275 void
276 openafs_remque(struct TM_Elem *elementp)
277 {
278     elementp->Next->Prev = elementp->Prev;
279     elementp->Prev->Next = elementp->Next;
280     elementp->Prev = elementp->Next = NULL;
281 }