rx: Return success value when cancelling an event
[openafs.git] / src / rx / rx_event.c
1 /*
2  * Copyright (c) 2011 Your File System Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 /* A reimplementation of the rx_event handler using red/black trees
26  *
27  * The first rx_event implementation used a simple sorted queue of all
28  * events, which lead to O(n^2) performance, where n is the number of
29  * outstanding events. This was found to scale poorly, so was replaced.
30  *
31  * The second implementation used a set of per-second buckets to store
32  * events. Each bucket (referred to as an epoch in the code) stored all
33  * of the events which expired in that second. However, on modern networks
34  * where RTT times are in the millisecond, most connections will have events
35  * expiring within the next second, so the problem reoccurs.
36  *
37  * This new implementation uses Red-Black trees to store a sorted list of
38  * events. Red Black trees are guaranteed to have no worse than O(log N)
39  * insertion, and are commonly used in timer applications
40  */
41
42 #include <afsconfig.h>
43 #include <afs/param.h>
44
45 #ifdef KERNEL
46 # include "afs/sysincludes.h"
47 # include "afsincludes.h"
48 #else
49 # include <roken.h>
50 #endif
51
52 #include <afs/opr.h>
53 #include <opr/queue.h>
54 #include <opr/rbtree.h>
55
56 #include "rx.h"
57 #include "rx_atomic.h"
58 #include "rx_call.h"
59 #include "rx_globals.h"
60
61 struct rxevent {
62     struct opr_queue q;
63     struct opr_rbtree_node node;
64     struct clock eventTime;
65     struct rxevent *next;
66     rx_atomic_t refcnt;
67     int handled;
68     void (*func)(struct rxevent *, void *, void *, int);
69     void *arg;
70     void *arg1;
71     int arg2;
72 };
73
74 struct malloclist {
75     void *mem;
76     int size;
77     struct malloclist *next;
78 };
79
80 static struct {
81     afs_kmutex_t lock;
82     struct opr_queue list;
83     struct malloclist *mallocs;
84 } freeEvents;
85
86 static struct {
87     afs_kmutex_t lock;
88     struct opr_rbtree head;
89     struct rxevent *first;
90 } eventTree;
91
92 static struct {
93     afs_kmutex_t lock;
94     struct clock last;
95     struct clock next;
96     void (*func)(void);
97     int raised;
98 } eventSchedule;
99
100 static int allocUnit = 10;
101
102 static struct rxevent *
103 rxevent_alloc(void) {
104      struct rxevent *evlist;
105      struct rxevent *ev;
106      struct malloclist *mrec;
107      int i;
108
109      MUTEX_ENTER(&freeEvents.lock);
110      if (opr_queue_IsEmpty(&freeEvents.list)) {
111         MUTEX_EXIT(&freeEvents.lock);
112
113 #if     defined(AFS_AIX32_ENV) && defined(KERNEL)
114         ev = rxi_Alloc(sizeof(struct rxevent));
115 #else
116         evlist = osi_Alloc(sizeof(struct rxevent) * allocUnit);
117         mrec = osi_Alloc(sizeof(struct malloclist));
118
119         mrec->mem = evlist;
120         mrec->size = sizeof(struct rxevent) * allocUnit;
121
122         MUTEX_ENTER(&freeEvents.lock);
123         for (i = 1; i < allocUnit; i++) {
124             opr_queue_Append(&freeEvents.list, &evlist[i].q);
125         }
126         mrec->next = freeEvents.mallocs;
127         freeEvents.mallocs = mrec;
128         MUTEX_EXIT(&freeEvents.lock);
129 #endif
130         ev = &evlist[0];
131     } else {
132         ev = opr_queue_First(&freeEvents.list, struct rxevent, q);
133         opr_queue_Remove(&ev->q);
134         MUTEX_EXIT(&freeEvents.lock);
135     }
136
137     memset(ev, 0, sizeof(struct rxevent));
138     rx_atomic_set(&ev->refcnt, 1);
139
140     return ev;
141 }
142
143 static void
144 rxevent_free(struct rxevent *ev) {
145     MUTEX_ENTER(&freeEvents.lock);
146     opr_queue_Prepend(&freeEvents.list, &ev->q);
147     MUTEX_EXIT(&freeEvents.lock);
148 }
149
150 static_inline void
151 rxevent_put(struct rxevent *ev) {
152     if (rx_atomic_dec_and_read(&ev->refcnt) == 0) {
153         rxevent_free(ev);
154     }
155 }
156
157 void
158 rxevent_Put(struct rxevent *ev) {
159     rxevent_put(ev);
160 }
161
162 static_inline struct rxevent *
163 rxevent_get(struct rxevent *ev) {
164     rx_atomic_inc(&ev->refcnt);
165     return ev;
166 }
167
168 struct rxevent *
169 rxevent_Get(struct rxevent *ev) {
170     return rxevent_get(ev);
171 }
172
173 /* Called if the time now is older than the last time we recorded running an
174  * event. This test catches machines where the system time has been set
175  * backwards, and avoids RX completely stalling when timers fail to fire.
176  *
177  * Take the different between now and the last event time, and subtract that
178  * from the timing of every event on the system. This does a relatively slow
179  * walk of the completely eventTree, but time-travel will hopefully be a pretty
180  * rare occurrence.
181  *
182  * This can only safely be called from the event thread, as it plays with the
183  * schedule directly.
184  *
185  */
186 static void
187 adjustTimes(void)
188 {
189     struct opr_rbtree_node *node;
190     struct clock adjTime, now;
191
192     MUTEX_ENTER(&eventTree.lock);
193     /* Time adjustment is expensive, make absolutely certain that we have
194      * to do it, by getting an up to date time to base our decision on
195      * once we've acquired the relevant locks.
196      */
197     clock_GetTime(&now);
198     if (!clock_Lt(&now, &eventSchedule.last))
199         goto out;
200
201     adjTime = eventSchedule.last;
202     clock_Zero(&eventSchedule.last);
203
204     clock_Sub(&adjTime, &now);
205
206     /* If there are no events in the tree, then there's nothing to adjust */
207     if (eventTree.first == NULL)
208         goto out;
209
210     node = opr_rbtree_first(&eventTree.head);
211     while(node) {
212         struct rxevent *event = opr_containerof(node, struct rxevent, node);
213
214         clock_Sub(&event->eventTime, &adjTime);
215         node = opr_rbtree_next(node);
216     }
217     eventSchedule.next = eventTree.first->eventTime;
218
219 out:
220     MUTEX_EXIT(&eventTree.lock);
221 }
222
223 static int initialised = 0;
224 void
225 rxevent_Init(int nEvents, void (*scheduler)(void))
226 {
227     if (initialised)
228         return;
229
230     initialised = 1;
231
232     clock_Init();
233     MUTEX_INIT(&eventTree.lock, "event tree lock", MUTEX_DEFAULT, 0);
234     opr_rbtree_init(&eventTree.head);
235
236     MUTEX_INIT(&freeEvents.lock, "free events lock", MUTEX_DEFAULT, 0);
237     opr_queue_Init(&freeEvents.list);
238     freeEvents.mallocs = NULL;
239
240     if (nEvents)
241         allocUnit = nEvents;
242
243     clock_Zero(&eventSchedule.next);
244     clock_Zero(&eventSchedule.last);
245     eventSchedule.raised = 0;
246     eventSchedule.func = scheduler;
247 }
248
249 struct rxevent *
250 rxevent_Post(struct clock *when, struct clock *now,
251              void (*func) (struct rxevent *, void *, void *, int),
252              void *arg, void *arg1, int arg2)
253 {
254     struct rxevent *ev, *event;
255     struct opr_rbtree_node **childptr, *parent = NULL;
256
257     ev = rxevent_alloc();
258     ev->eventTime = *when;
259     ev->func = func;
260     ev->arg = arg;
261     ev->arg1 = arg1;
262     ev->arg2 = arg2;
263
264     if (clock_Lt(now, &eventSchedule.last))
265         adjustTimes();
266
267     MUTEX_ENTER(&eventTree.lock);
268
269     /* Work out where in the tree we'll be storing this */
270     childptr = &eventTree.head.root;
271
272     while(*childptr) {
273         event = opr_containerof((*childptr), struct rxevent, node);
274
275         parent = *childptr;
276         if (clock_Lt(when, &event->eventTime))
277             childptr = &(*childptr)->left;
278         else if (clock_Gt(when, &event->eventTime))
279             childptr = &(*childptr)->right;
280         else {
281             opr_queue_Append(&event->q, &ev->q);
282             goto out;
283         }
284     }
285     opr_queue_Init(&ev->q);
286     opr_rbtree_insert(&eventTree.head, parent, childptr, &ev->node);
287
288     if (eventTree.first == NULL ||
289         clock_Lt(when, &(eventTree.first->eventTime))) {
290         eventTree.first = ev;
291         eventSchedule.raised = 1;
292         clock_Zero(&eventSchedule.next);
293         MUTEX_EXIT(&eventTree.lock);
294         if (eventSchedule.func != NULL)
295             (*eventSchedule.func)();
296         return rxevent_get(ev);
297     }
298
299 out:
300     MUTEX_EXIT(&eventTree.lock);
301     return rxevent_get(ev);
302 }
303
304 /* We're going to remove ev from the tree, so set the first pointer to the
305  * next event after it */
306 static_inline void
307 resetFirst(struct rxevent *ev)
308 {
309     struct opr_rbtree_node *next = opr_rbtree_next(&ev->node);
310     if (next)
311         eventTree.first = opr_containerof(next, struct rxevent, node);
312     else
313         eventTree.first = NULL;
314 }
315
316 /*!
317  * Cancel an event
318  *
319  * Cancels the event pointed to by evp. Returns true if the event has
320  * been succesfully cancelled, or false if the event has already fired.
321  */
322
323 int
324 rxevent_Cancel(struct rxevent **evp)
325 {
326     struct rxevent *event;
327     int cancelled = 0;
328
329     if (!evp || !*evp)
330         return 0;
331
332     event = *evp;
333
334     MUTEX_ENTER(&eventTree.lock);
335
336     if (!event->handled) {
337         /* We're a node on the red/black tree. If our list is non-empty,
338          * then swap the first element in the list in in our place,
339          * promoting it to the list head */
340         if (event->node.parent == NULL
341             && eventTree.head.root != &event->node) {
342             /* Not in the rbtree, therefore must be a list element */
343             opr_queue_Remove(&event->q);
344         } else {
345             if (!opr_queue_IsEmpty(&event->q)) {
346                 struct rxevent *next;
347
348                 next = opr_queue_First(&event->q, struct rxevent, q);
349                 opr_queue_Remove(&next->q); /* Remove ourselves from list */
350                 if (event->q.prev == &event->q) {
351                     next->q.prev = next->q.next = &next->q;
352                 } else {
353                     next->q = event->q;
354                     next->q.prev->next = &next->q;
355                     next->q.next->prev = &next->q;
356                 }
357
358                 opr_rbtree_replace(&eventTree.head, &event->node,
359                                    &next->node);
360
361                 if (eventTree.first == event)
362                     eventTree.first = next;
363
364             } else {
365                 if (eventTree.first == event)
366                     resetFirst(event);
367
368                 opr_rbtree_remove(&eventTree.head, &event->node);
369             }
370         }
371         event->handled = 1;
372         rxevent_put(event); /* Dispose of eventTree reference */
373         cancelled = 1;
374     }
375
376     MUTEX_EXIT(&eventTree.lock);
377
378     *evp = NULL;
379     rxevent_put(event); /* Dispose of caller's reference */
380
381     return cancelled;
382 }
383
384 /* Process all events which have expired. If events remain, then the relative
385  * time until the next event is returned in the parameter 'wait', and the
386  * function returns 1. If no events currently remain, the function returns 0
387  *
388  * If the current time is older than that of the last event processed, then we
389  * assume that time has gone backwards (for example, due to a system time reset)
390  * When this happens, all events in the current queue are rescheduled, using
391  * the difference between the current time and the last event time as a delta
392  */
393
394 int
395 rxevent_RaiseEvents(struct clock *wait)
396 {
397     struct clock now;
398     struct rxevent *event;
399     int ret;
400
401     clock_GetTime(&now);
402
403     /* Check for time going backwards */
404     if (clock_Lt(&now, &eventSchedule.last))
405           adjustTimes();
406     eventSchedule.last = now;
407
408     MUTEX_ENTER(&eventTree.lock);
409     /* Lock our event tree */
410     while (eventTree.first != NULL
411            && clock_Lt(&eventTree.first->eventTime, &now)) {
412
413         /* Grab the next node, either in the event's list, or in the tree node
414          * itself, and remove it from the event tree */
415         event = eventTree.first;
416         if (!opr_queue_IsEmpty(&event->q)) {
417             event = opr_queue_Last(&event->q, struct rxevent, q);
418             opr_queue_Remove(&event->q);
419         } else {
420             resetFirst(event);
421             opr_rbtree_remove(&eventTree.head, &event->node);
422         }
423         event->handled = 1;
424         MUTEX_EXIT(&eventTree.lock);
425
426         /* Fire the event, then free the structure */
427         event->func(event, event->arg, event->arg1, event->arg2);
428         rxevent_put(event);
429
430         MUTEX_ENTER(&eventTree.lock);
431     }
432
433     /* Figure out when we next need to be scheduled */
434     if (eventTree.first != NULL) {
435         *wait = eventSchedule.next = eventTree.first->eventTime;
436         ret = eventSchedule.raised = 1;
437         clock_Sub(wait, &now);
438     } else {
439         ret = eventSchedule.raised = 0;
440     }
441
442     MUTEX_EXIT(&eventTree.lock);
443
444     return ret;
445 }
446
447 void
448 shutdown_rxevent(void)
449 {
450     struct malloclist *mrec, *nmrec;
451
452     if (!initialised) {
453         return;
454     }
455     MUTEX_DESTROY(&eventTree.lock);
456
457 #if !defined(AFS_AIX32_ENV) || !defined(KERNEL)
458     MUTEX_DESTROY(&freeEvents.lock);
459     mrec = freeEvents.mallocs;
460     while (mrec) {
461         nmrec = mrec->next;
462         osi_Free(mrec->mem, mrec->size);
463         osi_Free(mrec, sizeof(struct malloclist));
464         mrec = nmrec;
465     }
466     mrec = NULL;
467 #endif
468 }