Remove the RCSID macro
[openafs.git] / src / rx / rx_event.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 #include <afsconfig.h>
11 #ifdef  KERNEL
12 #include "afs/param.h"
13 #else
14 #include <afs/param.h>
15 #endif
16
17 #ifdef AFS_SUN59_ENV
18 #include <sys/time_impl.h>
19 #endif
20
21
22 #ifdef KERNEL
23 #ifndef UKERNEL
24 #include "afs/afs_osi.h"
25 #else /* !UKERNEL */
26 #include "afs/sysincludes.h"
27 #include "afsincludes.h"
28 #endif /* !UKERNEL */
29 #include "rx/rx_clock.h"
30 #include "rx/rx_queue.h"
31 #include "rx/rx_event.h"
32 #include "rx/rx_kernel.h"
33 #include "rx_kmutex.h"
34 #ifdef RX_ENABLE_LOCKS
35 #include "rx/rx.h"
36 #endif /* RX_ENABLE_LOCKS */
37 #include "rx/rx_globals.h"
38 #if defined(AFS_SGI_ENV)
39 #include "sys/debug.h"
40 /* These are necessary to get curproc (used by GLOCK asserts) to work. */
41 #include "h/proc.h"
42 #if !defined(AFS_SGI64_ENV) && !defined(UKERNEL)
43 #include "h/user.h"
44 #endif
45 extern void *osi_Alloc();
46 #endif
47 #if defined(AFS_OBSD_ENV)
48 #include "h/proc.h"
49 #endif
50 #else /* KERNEL */
51 #include <stdio.h>
52 #include "rx_clock.h"
53 #include "rx_queue.h"
54 #include "rx_event.h"
55 #include "rx_user.h"
56 #ifdef AFS_PTHREAD_ENV
57 #include <rx/rx_pthread.h>
58 #else
59 #include "rx_lwp.h"
60 #endif
61 #ifdef RX_ENABLE_LOCKS
62 #include "rx.h"
63 #endif /* RX_ENABLE_LOCKS */
64 #include "rx_globals.h"
65 #ifdef AFS_NT40_ENV
66 #include <malloc.h>
67 #endif
68 #endif /* KERNEL */
69
70
71 /* All event processing is relative to the apparent current time given by clock_GetTime */
72
73 /* This should be static, but event_test wants to look at the free list... */
74 struct rx_queue rxevent_free;   /* It's somewhat bogus to use a doubly-linked queue for the free list */
75 struct rx_queue rxepoch_free;   /* It's somewhat bogus to use a doubly-linked queue for the free list */
76 static struct rx_queue rxepoch_queue;   /* list of waiting epochs */
77 static int rxevent_allocUnit = 10;      /* Allocation unit (number of event records to allocate at one time) */
78 static int rxepoch_allocUnit = 10;      /* Allocation unit (number of epoch records to allocate at one time) */
79 int rxevent_nFree;              /* Number of free event records */
80 int rxevent_nPosted;            /* Current number of posted events */
81 int rxepoch_nFree;              /* Number of free epoch records */
82 static void (*rxevent_ScheduledEarlierEvent) (void);    /* Proc to call when an event is scheduled that is earlier than all other events */
83 struct xfreelist {
84     void *mem;
85     int size;
86     struct xfreelist *next;
87 };
88 static struct xfreelist *xfreemallocs = 0, *xsp = 0;
89
90 struct clock rxevent_nextRaiseEvents;   /* Time of next call to raise events */
91 struct clock rxevent_lastEvent;        /* backwards time detection */
92 int rxevent_raiseScheduled;     /* true if raise events is scheduled */
93
94 #ifdef RX_ENABLE_LOCKS
95 #ifdef RX_LOCKS_DB
96 /* rxdb_fileID is used to identify the lock location, along with line#. */
97 static int rxdb_fileID = RXDB_FILE_RX_EVENT;
98 #endif /* RX_LOCKS_DB */
99 #define RX_ENABLE_LOCKS  1
100 afs_kmutex_t rxevent_lock;
101 #endif /* RX_ENABLE_LOCKS */
102
103 #ifdef AFS_PTHREAD_ENV
104 /*
105  * This mutex protects the following global variables:
106  * rxevent_initialized
107  */
108
109 #include <assert.h>
110 afs_kmutex_t rx_event_mutex;
111 #define LOCK_EV_INIT MUTEX_ENTER(&rx_event_mutex)
112 #define UNLOCK_EV_INIT MUTEX_EXIT(&rx_event_mutex)
113 #else
114 #define LOCK_EV_INIT
115 #define UNLOCK_EV_INIT
116 #endif /* AFS_PTHREAD_ENV */
117
118
119 int
120 rxevent_adjTimes(struct clock *adjTime)
121 {
122     /* backwards clock correction */
123     int nAdjusted = 0;
124     struct rxepoch *qep, *nqep;
125     struct rxevent *qev, *nqev;
126     
127     for (queue_Scan(&rxepoch_queue, qep, nqep, rxepoch)) {
128         for (queue_Scan(&qep->events, qev, nqev, rxevent)) {
129             if (clock_Gt(&qev->eventTime, adjTime)) {
130                 clock_Sub(&qev->eventTime, adjTime); 
131                 nAdjusted++;
132             }
133         }
134         if (qep->epochSec > adjTime->sec) {
135             qep->epochSec -= adjTime->sec;
136         }
137     }
138     return nAdjusted;
139 }
140
141 /* Pass in the number of events to allocate at a time */
142 int rxevent_initialized = 0;
143 void
144 rxevent_Init(int nEvents, void (*scheduler) (void))
145 {
146     LOCK_EV_INIT;
147     if (rxevent_initialized) {
148         UNLOCK_EV_INIT;
149         return;
150     }
151     MUTEX_INIT(&rxevent_lock, "rxevent_lock", MUTEX_DEFAULT, 0);
152     clock_Init();
153     if (nEvents)
154         rxevent_allocUnit = nEvents;
155     queue_Init(&rxevent_free);
156     queue_Init(&rxepoch_free);
157     queue_Init(&rxepoch_queue);
158     rxevent_nFree = rxevent_nPosted = 0;
159     rxepoch_nFree = 0;
160     rxevent_ScheduledEarlierEvent = scheduler;
161     rxevent_initialized = 1;
162     clock_Zero(&rxevent_nextRaiseEvents);
163     clock_Zero(&rxevent_lastEvent);
164     rxevent_raiseScheduled = 0;
165     UNLOCK_EV_INIT;
166 }
167
168 /* Create and initialize new epoch structure */
169 struct rxepoch *
170 rxepoch_Allocate(struct clock *when)
171 {
172     struct rxepoch *ep;
173     int i;
174
175     /* If we are short on free epoch entries, create a block of new oned
176      * and add them to the free queue */
177     if (queue_IsEmpty(&rxepoch_free)) {
178 #if    defined(AFS_AIX32_ENV) && defined(KERNEL)
179         ep = (struct rxepoch *)rxi_Alloc(sizeof(struct rxepoch));
180         queue_Append(&rxepoch_free, &ep[0]), rxepoch_nFree++;
181 #else
182 #if defined(KERNEL) && !defined(UKERNEL) && defined(AFS_FBSD80_ENV)
183         ep = (struct rxepoch *)
184             afs_osi_Alloc_NoSleep(sizeof(struct rxepoch) * rxepoch_allocUnit);
185         xsp = xfreemallocs;
186         xfreemallocs =
187             (struct xfreelist *)afs_osi_Alloc_NoSleep(sizeof(struct xfreelist));
188 #else
189         ep = (struct rxepoch *)
190             osi_Alloc(sizeof(struct rxepoch) * rxepoch_allocUnit);
191         xsp = xfreemallocs;
192         xfreemallocs =
193             (struct xfreelist *)osi_Alloc(sizeof(struct xfreelist));
194 #endif
195         xfreemallocs->mem = (void *)ep;
196         xfreemallocs->size = sizeof(struct rxepoch) * rxepoch_allocUnit;
197         xfreemallocs->next = xsp;
198         for (i = 0; i < rxepoch_allocUnit; i++)
199             queue_Append(&rxepoch_free, &ep[i]), rxepoch_nFree++;
200 #endif
201     }
202     ep = queue_First(&rxepoch_free, rxepoch);
203     queue_Remove(ep);
204     rxepoch_nFree--;
205     ep->epochSec = when->sec;
206     queue_Init(&ep->events);
207     return ep;
208 }
209
210 /* Add the indicated event (function, arg) at the specified clock time.  The
211  * "when" argument specifies when "func" should be called, in clock (clock.h)
212  * units. */
213
214 static struct rxevent *
215 _rxevent_Post(struct clock *when, struct clock *now, 
216               void (*func) (struct rxevent *, void *, void *, int), 
217               void *arg, void *arg1, int arg2, int newargs)
218 {
219     struct rxevent *ev, *evqe, *evqpr;
220     struct rxepoch *ep, *epqe, *epqpr;
221     int isEarliest = 0;
222
223     MUTEX_ENTER(&rxevent_lock);
224 #ifdef RXDEBUG
225     if (rx_Log_event) {
226         struct clock now1;
227         clock_GetTime(&now1);
228         fprintf(rx_Log_event, "%d.%d: rxevent_Post(%d.%d, %lp, %lp, %lp, %d)\n",
229                 (int)now1.sec, (int)now1.usec, (int)when->sec, (int)when->usec,
230                 func, arg,
231                 arg1, arg2);
232     }
233 #endif
234     /* If a time was provided, check for consistency */
235     if (now->sec) {
236         if (clock_Gt(&rxevent_lastEvent, now)) {
237             struct clock adjTime = rxevent_lastEvent;
238             clock_Sub(&adjTime, now);
239             rxevent_adjTimes(&adjTime);
240         }
241         rxevent_lastEvent = *now;
242     }
243     /* Get a pointer to the epoch for this event, if none is found then
244      * create a new epoch and insert it into the sorted list */
245     for (ep = NULL, queue_ScanBackwards(&rxepoch_queue, epqe, epqpr, rxepoch)) {
246         if (when->sec == epqe->epochSec) {
247             /* already have an structure for this epoch */
248             ep = epqe;
249             if (ep == queue_First(&rxepoch_queue, rxepoch))
250                 isEarliest = 1;
251             break;
252         } else if (when->sec > epqe->epochSec) {
253             /* Create a new epoch and insert after qe */
254             ep = rxepoch_Allocate(when);
255             queue_InsertAfter(epqe, ep);
256             break;
257         }
258     }
259     if (ep == NULL) {
260         /* Create a new epoch and place it at the head of the list */
261         ep = rxepoch_Allocate(when);
262         queue_Prepend(&rxepoch_queue, ep);
263         isEarliest = 1;
264     }
265
266     /* If we're short on free event entries, create a block of new ones and add
267      * them to the free queue */
268     if (queue_IsEmpty(&rxevent_free)) {
269         int i;
270 #if     defined(AFS_AIX32_ENV) && defined(KERNEL)
271         ev = (struct rxevent *)rxi_Alloc(sizeof(struct rxevent));
272         queue_Append(&rxevent_free, &ev[0]), rxevent_nFree++;
273 #else
274
275 #if defined(KERNEL) && !defined(UKERNEL) && defined(AFS_FBSD80_ENV)
276         ev = (struct rxevent *)afs_osi_Alloc_NoSleep(sizeof(struct rxevent) *
277                                          rxevent_allocUnit);
278         xsp = xfreemallocs;
279         xfreemallocs =
280             (struct xfreelist *)afs_osi_Alloc_NoSleep(sizeof(struct xfreelist));
281 #else
282         ev = (struct rxevent *)osi_Alloc(sizeof(struct rxevent) *
283                                          rxevent_allocUnit);
284         xsp = xfreemallocs;
285         xfreemallocs =
286             (struct xfreelist *)osi_Alloc(sizeof(struct xfreelist));
287 #endif
288         xfreemallocs->mem = (void *)ev;
289         xfreemallocs->size = sizeof(struct rxevent) * rxevent_allocUnit;
290         xfreemallocs->next = xsp;
291         for (i = 0; i < rxevent_allocUnit; i++)
292             queue_Append(&rxevent_free, &ev[i]), rxevent_nFree++;
293 #endif
294     }
295
296     /* Grab and initialize a new rxevent structure */
297     ev = queue_First(&rxevent_free, rxevent);
298     queue_Remove(ev);
299     rxevent_nFree--;
300
301     /* Record user defined event state */
302     ev->eventTime = *when;
303     if (newargs) {
304         ev->func.newfunc = func;
305     } else {
306         ev->func.oldfunc = (void (*)(struct rxevent *, void *, void*))func;
307     }
308     ev->arg = arg;
309     ev->arg1 = arg1;
310     ev->arg2 = arg2;
311     ev->newargs = newargs;
312     rxevent_nPosted += 1;       /* Rather than ++, to shut high-C up
313                                  *  regarding never-set variables
314                                  */
315
316     /* Insert the event into the sorted list of events for this epoch */
317     for (queue_ScanBackwards(&ep->events, evqe, evqpr, rxevent)) {
318         if (when->usec >= evqe->eventTime.usec) {
319             /* Insert event after evqe */
320             queue_InsertAfter(evqe, ev);
321             MUTEX_EXIT(&rxevent_lock);
322             return ev;
323         }
324     }
325     /* Insert event at head of current epoch */
326     queue_Prepend(&ep->events, ev);
327     if (isEarliest && rxevent_ScheduledEarlierEvent
328         && (!rxevent_raiseScheduled
329             || clock_Lt(&ev->eventTime, &rxevent_nextRaiseEvents))) {
330         rxevent_raiseScheduled = 1;
331         clock_Zero(&rxevent_nextRaiseEvents);
332         MUTEX_EXIT(&rxevent_lock);
333         /* Notify our external scheduler */
334         (*rxevent_ScheduledEarlierEvent) ();
335         MUTEX_ENTER(&rxevent_lock);
336     }
337     MUTEX_EXIT(&rxevent_lock);
338     return ev;
339 }
340
341 struct rxevent *
342 rxevent_Post(struct clock *when, 
343              void (*func) (struct rxevent *, void *, void *), 
344              void *arg, void *arg1)
345 {
346     struct clock now;
347     clock_Zero(&now);
348     return _rxevent_Post(when, &now, 
349                          (void (*)(struct rxevent *, void *, void *, int))func,
350                          arg, arg1, 0, 0);
351 }
352
353 struct rxevent *
354 rxevent_Post2(struct clock *when, 
355               void (*func) (struct rxevent *, void *, void *, int), 
356               void *arg, void *arg1, int arg2)
357 {
358     struct clock now;
359     clock_Zero(&now);
360     return _rxevent_Post(when, &now, func, arg, arg1, arg2, 1);
361 }
362
363 struct rxevent *
364 rxevent_PostNow(struct clock *when, struct clock *now, 
365                 void (*func) (struct rxevent *, void *, void *), 
366                 void *arg, void *arg1)
367 {
368     return _rxevent_Post(when, now, 
369                          (void (*)(struct rxevent *, void *, void *, int))func,
370                          arg, arg1, 0, 0);
371 }
372
373 struct rxevent *
374 rxevent_PostNow2(struct clock *when, struct clock *now, 
375                  void (*func) (struct rxevent *, void *, void *, int), 
376                  void *arg, void *arg1, int arg2)
377 {
378     return _rxevent_Post(when, now, func, arg, arg1, arg2, 1);
379 }
380
381 /* Cancel an event by moving it from the event queue to the free list.
382  * Warning, the event must be on the event queue!  If not, this should core
383  * dump (reference through 0).  This routine should be called using the macro
384  * event_Cancel, which checks for a null event and also nulls the caller's
385  * event pointer after cancelling the event.
386  */
387 #ifdef RX_ENABLE_LOCKS
388 #ifdef RX_REFCOUNT_CHECK
389 int rxevent_Cancel_type = 0;
390 #endif
391 #endif
392
393 void
394 rxevent_Cancel_1(struct rxevent *ev, struct rx_call *call,
395                  int type)
396 {
397 #ifdef RXDEBUG
398     if (rx_Log_event) {
399         struct clock now;
400         clock_GetTime(&now);
401         fprintf(rx_Log_event, "%d.%d: rxevent_Cancel_1(%d.%d, %lp, %lp)\n",
402                 (int)now.sec, (int)now.usec, (int)ev->eventTime.sec,
403                 (int)ev->eventTime.usec, ev->func.newfunc,
404                 ev->arg);
405     }
406 #endif
407     /* Append it to the free list (rather than prepending) to keep the free
408      * list hot so nothing pages out
409      */
410     MUTEX_ENTER(&rxevent_lock);
411     if (!ev) {
412         MUTEX_EXIT(&rxevent_lock);
413         return;
414     }
415 #ifdef RX_ENABLE_LOCKS
416     /* It's possible we're currently processing this event. */
417     if (queue_IsOnQueue(ev)) {
418         queue_MoveAppend(&rxevent_free, ev);
419         rxevent_nPosted--;
420         rxevent_nFree++;
421         if (call) {
422             call->refCount--;
423 #ifdef RX_REFCOUNT_CHECK
424             call->refCDebug[type]--;
425             if (call->refCDebug[type] < 0) {
426                 rxevent_Cancel_type = type;
427                 osi_Panic("rxevent_Cancel: call refCount < 0");
428             }
429 #endif /* RX_REFCOUNT_CHECK */
430         }
431     }
432 #else /* RX_ENABLE_LOCKS */
433     queue_MoveAppend(&rxevent_free, ev);
434     rxevent_nPosted--;
435     rxevent_nFree++;
436 #endif /* RX_ENABLE_LOCKS */
437     MUTEX_EXIT(&rxevent_lock);
438 }
439
440 /* Process all epochs that have expired relative to the current clock time
441  * (which is not re-evaluated unless clock_NewTime has been called).  The
442  * relative time to the next epoch is returned in the output parameter next
443  * and the function returns 1.  If there are is no next epoch, the function
444  * returns 0.
445  */
446 int
447 rxevent_RaiseEvents(struct clock *next)
448 {
449     struct rxepoch *ep;
450     struct rxevent *ev;
451     volatile struct clock now;
452     MUTEX_ENTER(&rxevent_lock);
453
454     /* Events are sorted by time, so only scan until an event is found that has
455      * not yet timed out */
456
457     clock_Zero(&now);
458     while (queue_IsNotEmpty(&rxepoch_queue)) {
459         ep = queue_First(&rxepoch_queue, rxepoch);
460         if (queue_IsEmpty(&ep->events)) {
461             queue_Remove(ep);
462             queue_Append(&rxepoch_free, ep);
463             rxepoch_nFree++;
464             continue;
465         }
466         do {
467         reraise:
468             ev = queue_First(&ep->events, rxevent);
469             if (clock_Lt(&now, &ev->eventTime)) {
470                 clock_GetTime(&now);
471                 if (clock_Gt(&rxevent_lastEvent, &now)) {
472                     struct clock adjTime = rxevent_lastEvent;
473                     int adjusted;
474                     clock_Sub(&adjTime, &now);
475                     adjusted = rxevent_adjTimes(&adjTime);
476                     rxevent_lastEvent = now;
477                     if (adjusted > 0)
478                         goto reraise;
479                 }
480                 if (clock_Lt(&now, &ev->eventTime)) {
481                     *next = rxevent_nextRaiseEvents = ev->eventTime;
482                     rxevent_raiseScheduled = 1;
483                     clock_Sub(next, &now);
484                     MUTEX_EXIT(&rxevent_lock);
485                     return 1;
486                 }
487             }
488             queue_Remove(ev);
489             rxevent_nPosted--;
490             MUTEX_EXIT(&rxevent_lock);
491             if (ev->newargs) {
492                 ev->func.newfunc(ev, ev->arg, ev->arg1, ev->arg2);
493             } else {
494                 ev->func.oldfunc(ev, ev->arg, ev->arg1);
495             }
496             MUTEX_ENTER(&rxevent_lock);
497             queue_Append(&rxevent_free, ev);
498             rxevent_nFree++;
499         } while (queue_IsNotEmpty(&ep->events));
500     }
501 #ifdef RXDEBUG
502     if (rx_Log_event)
503         fprintf(rx_Log_event, "rxevent_RaiseEvents(%d.%d)\n", (int)now.sec,
504                 (int)now.usec);
505 #endif
506     rxevent_raiseScheduled = 0;
507     MUTEX_EXIT(&rxevent_lock);
508     return 0;
509 }
510
511 void
512 shutdown_rxevent(void)
513 {
514     struct xfreelist *xp, *nxp;
515
516     LOCK_EV_INIT;
517     if (!rxevent_initialized) {
518         UNLOCK_EV_INIT;
519         return;
520     }
521     rxevent_initialized = 0;
522     UNLOCK_EV_INIT;
523     MUTEX_DESTROY(&rxevent_lock);
524 #if     defined(AFS_AIX32_ENV) && defined(KERNEL)
525     /* Everything is freed in afs_osinet.c */
526 #else
527     xp = xfreemallocs;
528     while (xp) {
529         nxp = xp->next;
530         osi_Free((char *)xp->mem, xp->size);
531         osi_Free((char *)xp, sizeof(struct xfreelist));
532         xp = nxp;
533     }
534     xfreemallocs = NULL;
535 #endif
536
537 }