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