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