rx-dewarnify-20041202
[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 int rxevent_raiseScheduled;     /* true if raise events is scheduled */
94
95 #ifdef RX_ENABLE_LOCKS
96 #ifdef RX_LOCKS_DB
97 /* rxdb_fileID is used to identify the lock location, along with line#. */
98 static int rxdb_fileID = RXDB_FILE_RX_EVENT;
99 #endif /* RX_LOCKS_DB */
100 #define RX_ENABLE_LOCKS  1
101 afs_kmutex_t rxevent_lock;
102 #endif /* RX_ENABLE_LOCKS */
103
104 #ifdef AFS_PTHREAD_ENV
105 /*
106  * This mutex protects the following global variables:
107  * rxevent_initialized
108  */
109
110 #include <assert.h>
111 pthread_mutex_t rx_event_mutex;
112 #define LOCK_EV_INIT assert(pthread_mutex_lock(&rx_event_mutex)==0)
113 #define UNLOCK_EV_INIT assert(pthread_mutex_unlock(&rx_event_mutex)==0)
114 #else
115 #define LOCK_EV_INIT
116 #define UNLOCK_EV_INIT
117 #endif /* AFS_PTHREAD_ENV */
118
119
120 /* Pass in the number of events to allocate at a time */
121 int rxevent_initialized = 0;
122 void
123 rxevent_Init(int nEvents, void (*scheduler) (void))
124 {
125     LOCK_EV_INIT;
126     if (rxevent_initialized) {
127         UNLOCK_EV_INIT;
128         return;
129     }
130     MUTEX_INIT(&rxevent_lock, "rxevent_lock", MUTEX_DEFAULT, 0);
131     clock_Init();
132     if (nEvents)
133         rxevent_allocUnit = nEvents;
134     queue_Init(&rxevent_free);
135     queue_Init(&rxepoch_free);
136     queue_Init(&rxepoch_queue);
137     rxevent_nFree = rxevent_nPosted = 0;
138     rxepoch_nFree = 0;
139     rxevent_ScheduledEarlierEvent = scheduler;
140     rxevent_initialized = 1;
141     clock_Zero(&rxevent_nextRaiseEvents);
142     rxevent_raiseScheduled = 0;
143     UNLOCK_EV_INIT;
144 }
145
146 /* Create and initialize new epoch structure */
147 struct rxepoch *
148 rxepoch_Allocate(struct clock *when)
149 {
150     struct rxepoch *ep;
151     int i;
152
153     /* If we are short on free epoch entries, create a block of new oned
154      * and add them to the free queue */
155     if (queue_IsEmpty(&rxepoch_free)) {
156 #if    defined(AFS_AIX32_ENV) && defined(KERNEL)
157         ep = (struct rxepoch *)rxi_Alloc(sizeof(struct rxepoch));
158         queue_Append(&rxepoch_free, &ep[0]), rxepoch_nFree++;
159 #else
160         ep = (struct rxepoch *)
161             osi_Alloc(sizeof(struct rxepoch) * rxepoch_allocUnit);
162         xsp = xfreemallocs;
163         xfreemallocs =
164             (struct xfreelist *)osi_Alloc(sizeof(struct xfreelist));
165         xfreemallocs->mem = (void *)ep;
166         xfreemallocs->size = sizeof(struct rxepoch) * rxepoch_allocUnit;
167         xfreemallocs->next = xsp;
168         for (i = 0; i < rxepoch_allocUnit; i++)
169             queue_Append(&rxepoch_free, &ep[i]), rxepoch_nFree++;
170 #endif
171     }
172     ep = queue_First(&rxepoch_free, rxepoch);
173     queue_Remove(ep);
174     rxepoch_nFree--;
175     ep->epochSec = when->sec;
176     queue_Init(&ep->events);
177     return ep;
178 }
179
180 /* Add the indicated event (function, arg) at the specified clock time.  The
181  * "when" argument specifies when "func" should be called, in clock (clock.h)
182  * units. */
183
184 #if 0
185 struct rxevent *
186 rxevent_Post(struct clock *when,
187              void (*func) (struct rxevent * event,
188                            struct rx_connection * conn,
189                            struct rx_call * acall), void *arg, void *arg1)
190 #else
191 static struct rxevent *
192 _rxevent_Post(struct clock *when, void (*func) (), void *arg, void *arg1,
193               int arg2, int newargs)
194 #endif
195 {
196     register struct rxevent *ev, *evqe, *evqpr;
197     register struct rxepoch *ep, *epqe, *epqpr;
198     int isEarliest = 0;
199
200     MUTEX_ENTER(&rxevent_lock);
201     AFS_ASSERT_RXGLOCK();
202 #ifdef RXDEBUG
203     if (rx_Log_event) {
204         struct clock now;
205         clock_GetTime(&now);
206         fprintf(rx_Log_event, "%d.%d: rxevent_Post(%d.%d, %lx, %lx, %lx, %d)\n",
207                 (int)now.sec, (int)now.usec, (int)when->sec, (int)when->usec,
208                 (unsigned long)func, (unsigned long)arg,
209                 (unsigned long)arg1, arg2);
210     }
211 #endif
212
213     /* Get a pointer to the epoch for this event, if none is found then
214      * create a new epoch and insert it into the sorted list */
215     for (ep = NULL, queue_ScanBackwards(&rxepoch_queue, epqe, epqpr, rxepoch)) {
216         if (when->sec == epqe->epochSec) {
217             /* already have an structure for this epoch */
218             ep = epqe;
219             if (ep == queue_First(&rxepoch_queue, rxepoch))
220                 isEarliest = 1;
221             break;
222         } else if (when->sec > epqe->epochSec) {
223             /* Create a new epoch and insert after qe */
224             ep = rxepoch_Allocate(when);
225             queue_InsertAfter(epqe, ep);
226             break;
227         }
228     }
229     if (ep == NULL) {
230         /* Create a new epoch and place it at the head of the list */
231         ep = rxepoch_Allocate(when);
232         queue_Prepend(&rxepoch_queue, ep);
233         isEarliest = 1;
234     }
235
236     /* If we're short on free event entries, create a block of new ones and add
237      * them to the free queue */
238     if (queue_IsEmpty(&rxevent_free)) {
239         register int i;
240 #if     defined(AFS_AIX32_ENV) && defined(KERNEL)
241         ev = (struct rxevent *)rxi_Alloc(sizeof(struct rxevent));
242         queue_Append(&rxevent_free, &ev[0]), rxevent_nFree++;
243 #else
244         ev = (struct rxevent *)osi_Alloc(sizeof(struct rxevent) *
245                                          rxevent_allocUnit);
246         xsp = xfreemallocs;
247         xfreemallocs =
248             (struct xfreelist *)osi_Alloc(sizeof(struct xfreelist));
249         xfreemallocs->mem = (void *)ev;
250         xfreemallocs->size = sizeof(struct rxevent) * rxevent_allocUnit;
251         xfreemallocs->next = xsp;
252         for (i = 0; i < rxevent_allocUnit; i++)
253             queue_Append(&rxevent_free, &ev[i]), rxevent_nFree++;
254 #endif
255     }
256
257     /* Grab and initialize a new rxevent structure */
258     ev = queue_First(&rxevent_free, rxevent);
259     queue_Remove(ev);
260     rxevent_nFree--;
261
262     /* Record user defined event state */
263     ev->eventTime = *when;
264     ev->func = func;
265     ev->arg = arg;
266     ev->arg1 = arg1;
267     ev->arg2 = arg2;
268     ev->newargs = newargs;
269     rxevent_nPosted += 1;       /* Rather than ++, to shut high-C up
270                                  *  regarding never-set variables
271                                  */
272
273     /* Insert the event into the sorted list of events for this epoch */
274     for (queue_ScanBackwards(&ep->events, evqe, evqpr, rxevent)) {
275         if (when->usec >= evqe->eventTime.usec) {
276             /* Insert event after evqe */
277             queue_InsertAfter(evqe, ev);
278             MUTEX_EXIT(&rxevent_lock);
279             return ev;
280         }
281     }
282     /* Insert event at head of current epoch */
283     queue_Prepend(&ep->events, ev);
284     if (isEarliest && rxevent_ScheduledEarlierEvent
285         && (!rxevent_raiseScheduled
286             || clock_Lt(&ev->eventTime, &rxevent_nextRaiseEvents))) {
287         rxevent_raiseScheduled = 1;
288         clock_Zero(&rxevent_nextRaiseEvents);
289         MUTEX_EXIT(&rxevent_lock);
290         /* Notify our external scheduler */
291         (*rxevent_ScheduledEarlierEvent) ();
292         MUTEX_ENTER(&rxevent_lock);
293     }
294     MUTEX_EXIT(&rxevent_lock);
295     return ev;
296 }
297
298 struct rxevent *
299 rxevent_Post(struct clock *when, void (*func) (), void *arg, void *arg1)
300 {
301     return _rxevent_Post(when, func, arg, arg1, 0, 0);
302 }
303
304 struct rxevent *
305 rxevent_Post2(struct clock *when, void (*func) (), void *arg, void *arg1,
306               int arg2)
307 {
308     return _rxevent_Post(when, func, arg, arg1, arg2, 1);
309 }
310
311 /* Cancel an event by moving it from the event queue to the free list.
312  * Warning, the event must be on the event queue!  If not, this should core
313  * dump (reference through 0).  This routine should be called using the macro
314  * event_Cancel, which checks for a null event and also nulls the caller's
315  * event pointer after cancelling the event.
316  */
317 #ifdef RX_ENABLE_LOCKS
318 #ifdef RX_REFCOUNT_CHECK
319 int rxevent_Cancel_type = 0;
320 #endif
321 #endif
322
323 void
324 rxevent_Cancel_1(register struct rxevent *ev, register struct rx_call *call,
325                  register int type)
326 {
327 #ifdef RXDEBUG
328     if (rx_Log_event) {
329         struct clock now;
330         clock_GetTime(&now);
331         fprintf(rx_Log_event, "%d.%d: rxevent_Cancel_1(%d.%d, %lx, %lx)\n",
332                 (int)now.sec, (int)now.usec, (int)ev->eventTime.sec,
333                 (int)ev->eventTime.usec, (unsigned long)ev->func,
334                 (unsigned long)ev->arg);
335     }
336 #endif
337     /* Append it to the free list (rather than prepending) to keep the free
338      * list hot so nothing pages out
339      */
340     AFS_ASSERT_RXGLOCK();
341     MUTEX_ENTER(&rxevent_lock);
342     if (!ev) {
343         MUTEX_EXIT(&rxevent_lock);
344         return;
345     }
346 #ifdef RX_ENABLE_LOCKS
347     /* It's possible we're currently processing this event. */
348     if (queue_IsOnQueue(ev)) {
349         queue_MoveAppend(&rxevent_free, ev);
350         rxevent_nPosted--;
351         rxevent_nFree++;
352         if (call) {
353             call->refCount--;
354 #ifdef RX_REFCOUNT_CHECK
355             call->refCDebug[type]--;
356             if (call->refCDebug[type] < 0) {
357                 rxevent_Cancel_type = type;
358                 osi_Panic("rxevent_Cancel: call refCount < 0");
359             }
360 #endif /* RX_REFCOUNT_CHECK */
361         }
362     }
363 #else /* RX_ENABLE_LOCKS */
364     queue_MoveAppend(&rxevent_free, ev);
365     rxevent_nPosted--;
366     rxevent_nFree++;
367 #endif /* RX_ENABLE_LOCKS */
368     MUTEX_EXIT(&rxevent_lock);
369 }
370
371 /* Process all epochs that have expired relative to the current clock time
372  * (which is not re-evaluated unless clock_NewTime has been called).  The
373  * relative time to the next epoch is returned in the output parameter next
374  * and the function returns 1.  If there are is no next epoch, the function
375  * returns 0.
376  */
377 int
378 rxevent_RaiseEvents(struct clock *next)
379 {
380     register struct rxepoch *ep;
381     register struct rxevent *ev;
382     volatile struct clock now;
383
384     MUTEX_ENTER(&rxevent_lock);
385
386     AFS_ASSERT_RXGLOCK();
387
388     /* Events are sorted by time, so only scan until an event is found that has
389      * not yet timed out */
390
391     clock_Zero(&now);
392     while (queue_IsNotEmpty(&rxepoch_queue)) {
393         ep = queue_First(&rxepoch_queue, rxepoch);
394         if (queue_IsEmpty(&ep->events)) {
395             queue_Remove(ep);
396             queue_Append(&rxepoch_free, ep);
397             rxepoch_nFree++;
398             continue;
399         }
400         do {
401             ev = queue_First(&ep->events, rxevent);
402             if (clock_Lt(&now, &ev->eventTime)) {
403                 clock_GetTime(&now);
404                 if (clock_Lt(&now, &ev->eventTime)) {
405                     *next = rxevent_nextRaiseEvents = ev->eventTime;
406                     rxevent_raiseScheduled = 1;
407                     clock_Sub(next, &now);
408                     MUTEX_EXIT(&rxevent_lock);
409                     return 1;
410                 }
411             }
412             queue_Remove(ev);
413             rxevent_nPosted--;
414             MUTEX_EXIT(&rxevent_lock);
415             if (ev->newargs) {
416                 ev->func(ev, ev->arg, ev->arg1, ev->arg2);
417             } else {
418                 ev->func(ev, ev->arg, ev->arg1);
419             }
420             MUTEX_ENTER(&rxevent_lock);
421             queue_Append(&rxevent_free, ev);
422             rxevent_nFree++;
423         } while (queue_IsNotEmpty(&ep->events));
424     }
425 #ifdef RXDEBUG
426     if (rx_Log_event)
427         fprintf(rx_Log_event, "rxevent_RaiseEvents(%d.%d)\n", (int)now.sec,
428                 (int)now.usec);
429 #endif
430     rxevent_raiseScheduled = 0;
431     MUTEX_EXIT(&rxevent_lock);
432     return 0;
433 }
434
435 void
436 shutdown_rxevent(void)
437 {
438     struct xfreelist *xp, *nxp;
439
440     LOCK_EV_INIT;
441     if (!rxevent_initialized) {
442         UNLOCK_EV_INIT;
443         return;
444     }
445     rxevent_initialized = 0;
446     UNLOCK_EV_INIT;
447     MUTEX_DESTROY(&rxevent_lock);
448 #if     defined(AFS_AIX32_ENV) && defined(KERNEL)
449     /* Everything is freed in afs_osinet.c */
450 #else
451     xp = xfreemallocs;
452     while (xp) {
453         nxp = xp->next;
454         osi_Free((char *)xp->mem, xp->size);
455         osi_Free((char *)xp, sizeof(struct xfreelist));
456         xp = nxp;
457     }
458     xfreemallocs = NULL;
459 #endif
460
461 }