1 #include <sys/time.h>
2
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "mpool.h"
7
8 #include "events.h"
9 #include "events_internal.h"
10
11 /* Event structure. */
12 struct eventrec {
13 int (*func)(void *);
14 void * cookie;
15 };
16
17 MPOOL(eventrec, struct eventrec, 4096);
18
19 /* Zero timeval, for use with non-blocking event runs. */
20 static const struct timeval tv_zero = {0, 0};
21
22 /**
23 * events_mkrec(func, cookie):
24 * Package ${func}, ${cookie} into a struct eventrec.
25 */
26 struct eventrec *
events_mkrec(int (* func)(void *),void * cookie)27 events_mkrec(int (*func)(void *), void * cookie)
28 {
29 struct eventrec * r;
30
31 /* Allocate structure. */
32 if ((r = mpool_eventrec_malloc()) == NULL)
33 goto err0;
34
35 /* Initialize. */
36 r->func = func;
37 r->cookie = cookie;
38
39 /* Success! */
40 return (r);
41
42 err0:
43 /* Failure! */
44 return (NULL);
45 }
46
47 /**
48 * events_freerec(r):
49 * Free the eventrec ${r}.
50 */
51 void
events_freerec(struct eventrec * r)52 events_freerec(struct eventrec * r)
53 {
54
55 mpool_eventrec_free(r);
56 }
57
58 /* Do an event. This makes events_run cleaner. */
59 static inline int
doevent(struct eventrec * r)60 doevent(struct eventrec * r)
61 {
62 int rc;
63
64 /* Invoke the callback. */
65 rc = (r->func)(r->cookie);
66
67 /* Free the event record. */
68 mpool_eventrec_free(r);
69
70 /* Return the status code from the callback. */
71 return (rc);
72 }
73
74 /**
75 * events_run(void):
76 * Run events. Events registered via events_immediate_register will be run
77 * first, in order of increasing ${prio} values; then events associated with
78 * ready sockets registered via events_network_register; finally, events
79 * associated with expired timers registered via events_timer_register will
80 * be run. If any event function returns a non-zero result, no further
81 * events will be run and said non-zero result will be returned; on error,
82 * -1 will be returned.
83 */
84 int
events_run(void)85 events_run(void)
86 {
87 struct eventrec * r;
88 struct timeval * tv;
89 struct timeval tv2;
90 int rc = 0;
91
92 /* If we have any immediate events, process them and return. */
93 if ((r = events_immediate_get()) != NULL) {
94 while (r != NULL) {
95 /* Process the event. */
96 if ((rc = doevent(r)) != 0)
97 goto done;
98
99 /* Get the next event. */
100 r = events_immediate_get();
101 }
102
103 /* We've processed at least one event; time to return. */
104 goto done;
105 }
106
107 /*
108 * Figure out the maximum duration to block, and wait up to that
109 * duration for network events to become available.
110 */
111 if (events_timer_min(&tv))
112 goto err0;
113 if (events_network_select(tv))
114 goto err1;
115 free(tv);
116
117 /*
118 * Check for available immediate events, network events, and timer
119 * events, in that order of priority; exit only when no more events
120 * are available.
121 */
122 do {
123 /* Run an immediate event, if one is available. */
124 if ((r = events_immediate_get()) != NULL) {
125 if ((rc = doevent(r)) != 0)
126 goto done;
127 continue;
128 }
129
130 /* Run a network event, if one is available. */
131 if ((r = events_network_get()) != NULL) {
132 if ((rc = doevent(r)) != 0)
133 goto done;
134 continue;
135 }
136
137 /* Check if any new network events are available. */
138 memcpy(&tv2, &tv_zero, sizeof(struct timeval));
139 if (events_network_select(&tv2))
140 goto err0;
141 if ((r = events_network_get()) != NULL) {
142 if ((rc = doevent(r)) != 0)
143 goto done;
144 continue;
145 }
146
147 /* Run a timer event, if one is available. */
148 if (events_timer_get(&r))
149 goto err0;
150 if (r != NULL) {
151 if ((rc = doevent(r)) != 0)
152 goto done;
153 continue;
154 }
155
156 /* No events available. */
157 break;
158 } while (1);
159
160 done:
161 /* Success! */
162 return (rc);
163
164 err1:
165 free(tv);
166 err0:
167 /* Failure! */
168 return (-1);
169 }
170
171 /**
172 * events_spin(done):
173 * Run events until ${done} is non-zero (and return 0), an error occurs (and
174 * return -1), or a callback returns a non-zero status (and return the status
175 * code from the callback).
176 */
177 int
events_spin(int * done)178 events_spin(int * done)
179 {
180 int rc = 0;
181
182 /* Loop until we're done or have a non-zero status. */
183 while ((done[0] == 0) && (rc == 0)) {
184 /* Run events. */
185 rc = events_run();
186 }
187
188 /* Return status code. */
189 return (rc);
190 }
191
192 /**
193 * events_shutdown(void):
194 * Clean up and free memory. This call is not necessary on program exit and
195 * is only expected to be useful when checking for memory leaks.
196 */
197 void
events_shutdown(void)198 events_shutdown(void)
199 {
200
201 events_network_shutdown();
202 events_timer_shutdown();
203 }
204