1 #include <assert.h>
2 #include <stdlib.h>
3 
4 #include "mpool.h"
5 
6 #include "events.h"
7 #include "events_internal.h"
8 
9 struct eventq {
10 	struct eventrec * r;
11 	struct eventq * next;
12 	struct eventq * prev;
13 	int prio;
14 };
15 
16 MPOOL(eventq, struct eventq, 4096);
17 
18 /* First nodes in the linked lists. */
19 static struct eventq * heads[32] = {
20 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
21 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
22 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
23 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
24 };
25 
26 /* For non-NULL heads[i], tails[i] is the last node in the list. */
27 static struct eventq * tails[32];
28 
29 /* For i < minq, heads[i] == NULL. */
30 static int minq = 32;
31 
32 /**
33  * events_immediate_register(func, cookie, prio):
34  * Register ${func}(${cookie}) to be run the next time events_run is invoked,
35  * after immediate events with smaller ${prio} values and before events with
36  * larger ${prio} values.  The value ${prio} must be in the range [0, 31].
37  * Return a cookie which can be passed to events_immediate_cancel.
38  */
39 void *
events_immediate_register(int (* func)(void *),void * cookie,int prio)40 events_immediate_register(int (*func)(void *), void * cookie, int prio)
41 {
42 	struct eventrec * r;
43 	struct eventq * q;
44 
45 	/* Sanity check. */
46 	assert((prio >= 0) && (prio < 32));
47 
48 	/* Bundle into an eventrec record. */
49 	if ((r = events_mkrec(func, cookie)) == NULL)
50 		goto err0;
51 
52 	/* Create a linked list node. */
53 	if ((q = mpool_eventq_malloc()) == NULL)
54 		goto err1;
55 	q->r = r;
56 	q->next = NULL;
57 	q->prev = NULL;
58 	q->prio = prio;
59 
60 	/* Add to the queue. */
61 	if (heads[prio] == NULL) {
62 		heads[prio] = q;
63 		if (prio < minq)
64 			minq = prio;
65 	} else {
66 		tails[prio]->next = q;
67 		q->prev = tails[prio];
68 	}
69 	tails[prio] = q;
70 
71 	/* Success! */
72 	return (q);
73 
74 err1:
75 	events_freerec(r);
76 err0:
77 	/* Failure! */
78 	return (NULL);
79 }
80 
81 /**
82  * events_immediate_cancel(cookie):
83  * Cancel the immediate event for which the cookie ${cookie} was returned by
84  * events_immediate_register.
85  */
86 void
events_immediate_cancel(void * cookie)87 events_immediate_cancel(void * cookie)
88 {
89 	struct eventq * q = cookie;
90 	int prio = q->prio;
91 
92 	/* If we have a predecessor, point it at our successor. */
93 	if (q->prev != NULL)
94 		q->prev->next = q->next;
95 	else
96 		heads[prio] = q->next;
97 
98 	/* If we have a successor, point it at our predecessor. */
99 	if (q->next != NULL)
100 		q->next->prev = q->prev;
101 	else
102 		tails[prio] = q->prev;
103 
104 	/* Free the eventrec. */
105 	events_freerec(q->r);
106 
107 	/* Return the node to the malloc pool. */
108 	mpool_eventq_free(q);
109 }
110 
111 /**
112  * events_immediate_get(void):
113  * Remove and return an eventrec structure from the immediate event queue,
114  * or return NULL if there are no such events.  The caller is responsible for
115  * freeing the returned memory.
116  */
117 struct eventrec *
events_immediate_get(void)118 events_immediate_get(void)
119 {
120 	struct eventq * q;
121 	struct eventrec * r;
122 
123 	/* Advance past priorities which have no events. */
124 	while ((minq < 32) && (heads[minq] == NULL))
125 		minq++;
126 
127 	/* Are there any events? */
128 	if (minq == 32)
129 		return (NULL);
130 
131 	/*
132 	 * Remove the first node from the highest priority non-empty linked
133 	 * list.
134 	 */
135 	q = heads[minq];
136 	heads[minq] = q->next;
137 	if (heads[minq] != NULL)
138 		heads[minq]->prev = NULL;
139 
140 	/* Extract the eventrec. */
141 	r = q->r;
142 
143 	/* Return the node to the malloc pool. */
144 	mpool_eventq_free(q);
145 
146 	/* Return the eventrec. */
147 	return (r);
148 }
149