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