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