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