r20846: Before this gets out of control...
[tprouty/samba.git] / source / lib / events.c
1 /*
2    Unix SMB/CIFS implementation.
3    Timed event library.
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Volker Lendecke 2005
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 struct timed_event {
25         struct timed_event *next, *prev;
26         struct event_context *event_ctx;
27         struct timeval when;
28         const char *event_name;
29         void (*handler)(struct event_context *event_ctx,
30                         struct timed_event *te,
31                         const struct timeval *now,
32                         void *private_data);
33         void *private_data;
34 };
35
36 struct fd_event {
37         struct fd_event *prev, *next;
38         struct event_context *event_ctx;
39         int fd;
40         uint16_t flags; /* see EVENT_FD_* flags */
41         void (*handler)(struct event_context *event_ctx,
42                         struct fd_event *event,
43                         uint16 flags,
44                         void *private_data);
45         void *private_data;
46 };
47
48 #define EVENT_FD_WRITEABLE(fde) \
49         event_set_fd_flags(fde, event_get_fd_flags(fde) | EVENT_FD_WRITE)
50 #define EVENT_FD_READABLE(fde) \
51         event_set_fd_flags(fde, event_get_fd_flags(fde) | EVENT_FD_READ)
52
53 #define EVENT_FD_NOT_WRITEABLE(fde) \
54         event_set_fd_flags(fde, event_get_fd_flags(fde) & ~EVENT_FD_WRITE)
55 #define EVENT_FD_NOT_READABLE(fde) \
56         event_set_fd_flags(fde, event_get_fd_flags(fde) & ~EVENT_FD_READ)
57
58 struct event_context {
59         struct timed_event *timed_events;
60         struct fd_event *fd_events;
61 };
62
63 static int timed_event_destructor(struct timed_event *te)
64 {
65         DEBUG(10, ("Destroying timed event %lx \"%s\"\n", (unsigned long)te,
66                 te->event_name));
67         DLIST_REMOVE(te->event_ctx->timed_events, te);
68         return 0;
69 }
70
71 /****************************************************************************
72  Schedule a function for future calling, cancel with TALLOC_FREE().
73  It's the responsibility of the handler to call TALLOC_FREE() on the event
74  handed to it.
75 ****************************************************************************/
76
77 struct timed_event *event_add_timed(struct event_context *event_ctx,
78                                     TALLOC_CTX *mem_ctx,
79                                 struct timeval when,
80                                 const char *event_name,
81                                 void (*handler)(struct event_context *event_ctx,
82                                                 struct timed_event *te,
83                                                 const struct timeval *now,
84                                                 void *private_data),
85                                 void *private_data)
86 {
87         struct timed_event *te, *last_te, *cur_te;
88
89         te = TALLOC_P(mem_ctx, struct timed_event);
90         if (te == NULL) {
91                 DEBUG(0, ("talloc failed\n"));
92                 return NULL;
93         }
94
95         te->event_ctx = event_ctx;
96         te->when = when;
97         te->event_name = event_name;
98         te->handler = handler;
99         te->private_data = private_data;
100
101         /* keep the list ordered - this is NOT guarenteed as event times
102            may be changed after insertion */
103         last_te = NULL;
104         for (cur_te = event_ctx->timed_events; cur_te; cur_te = cur_te->next) {
105                 /* if the new event comes before the current one break */
106                 if (!timeval_is_zero(&cur_te->when)
107                     && timeval_compare(&te->when, &cur_te->when) < 0) {
108                         break;
109                 }
110                 last_te = cur_te;
111         }
112
113         DLIST_ADD_AFTER(event_ctx->timed_events, te, last_te);
114         talloc_set_destructor(te, timed_event_destructor);
115
116         DEBUG(10, ("Added timed event \"%s\": %lx\n", event_name,
117                         (unsigned long)te));
118         return te;
119 }
120
121 static int fd_event_destructor(struct fd_event *fde)
122 {
123         struct event_context *event_ctx = fde->event_ctx;
124
125         DLIST_REMOVE(event_ctx->fd_events, fde);
126         return 0;
127 }
128
129 struct fd_event *event_add_fd(struct event_context *event_ctx,
130                               TALLOC_CTX *mem_ctx,
131                               int fd, uint16_t flags,
132                               void (*handler)(struct event_context *event_ctx,
133                                               struct fd_event *event,
134                                               uint16 flags,
135                                               void *private_data),
136                               void *private_data)
137 {
138         struct fd_event *fde;
139
140         if (!(fde = TALLOC_P(mem_ctx, struct fd_event))) {
141                 return NULL;
142         }
143
144         fde->event_ctx = event_ctx;
145         fde->fd = fd;
146         fde->flags = flags;
147         fde->handler = handler;
148         fde->private_data = private_data;
149
150         DLIST_ADD(event_ctx->fd_events, fde);
151
152         talloc_set_destructor(fde, fd_event_destructor);
153         return fde;
154 }
155
156 void event_fd_set_writeable(struct fd_event *fde)
157 {
158         fde->flags |= EVENT_FD_WRITE;
159 }
160
161 void event_fd_set_not_writeable(struct fd_event *fde)
162 {
163         fde->flags &= ~EVENT_FD_WRITE;
164 }
165
166 void event_fd_set_readable(struct fd_event *fde)
167 {
168         fde->flags |= EVENT_FD_READ;
169 }
170
171 void event_fd_set_not_readable(struct fd_event *fde)
172 {
173         fde->flags &= ~EVENT_FD_READ;
174 }
175
176 void event_add_to_select_args(struct event_context *event_ctx,
177                               const struct timeval *now,
178                               fd_set *read_fds, fd_set *write_fds,
179                               struct timeval *timeout, int *maxfd)
180 {
181         struct fd_event *fde;
182         struct timeval diff;
183
184         for (fde = event_ctx->fd_events; fde; fde = fde->next) {
185                 if (fde->flags & EVENT_FD_READ) {
186                         FD_SET(fde->fd, read_fds);
187                 }
188                 if (fde->flags & EVENT_FD_WRITE) {
189                         FD_SET(fde->fd, write_fds);
190                 }
191
192                 if ((fde->flags & (EVENT_FD_READ|EVENT_FD_WRITE))
193                     && (fde->fd > *maxfd)) {
194                         *maxfd = fde->fd;
195                 }
196         }
197
198         if (event_ctx->timed_events == NULL) {
199                 return;
200         }
201
202         diff = timeval_until(now, &event_ctx->timed_events->when);
203         *timeout = timeval_min(timeout, &diff);
204 }
205
206 BOOL run_events(struct event_context *event_ctx,
207                 int selrtn, fd_set *read_fds, fd_set *write_fds)
208 {
209         BOOL fired = False;
210         struct fd_event *fde, *next;
211
212         /* Run all events that are pending, not just one (as we
213            did previously. */
214
215         while (event_ctx->timed_events) {
216                 struct timeval now;
217                 GetTimeOfDay(&now);
218
219                 if (timeval_compare(
220                             &now, &event_ctx->timed_events->when) < 0) {
221                         /* Nothing to do yet */
222                         DEBUG(11, ("run_events: Nothing to do\n"));
223                         break;
224                 }
225
226                 DEBUG(10, ("Running event \"%s\" %lx\n",
227                            event_ctx->timed_events->event_name,
228                            (unsigned long)event_ctx->timed_events));
229
230                 event_ctx->timed_events->handler(
231                         event_ctx,
232                         event_ctx->timed_events, &now,
233                         event_ctx->timed_events->private_data);
234
235                 fired = True;
236         }
237
238         if (fired) {
239                 /*
240                  * We might have changed the socket status during the timed
241                  * events, return to run select again.
242                  */
243                 return True;
244         }
245
246         if (selrtn == 0) {
247                 /*
248                  * No fd ready
249                  */
250                 return fired;
251         }
252
253         for (fde = event_ctx->fd_events; fde; fde = next) {
254                 uint16 flags = 0;
255
256                 next = fde->next;
257                 if (FD_ISSET(fde->fd, read_fds)) flags |= EVENT_FD_READ;
258                 if (FD_ISSET(fde->fd, write_fds)) flags |= EVENT_FD_WRITE;
259
260                 if (flags) {
261                         fde->handler(event_ctx, fde, flags, fde->private_data);
262                         fired = True;
263                 }
264         }
265
266         return fired;
267 }
268
269
270 struct timeval *get_timed_events_timeout(struct event_context *event_ctx,
271                                          struct timeval *to_ret)
272 {
273         struct timeval now;
274
275         if (event_ctx->timed_events == NULL) {
276                 return NULL;
277         }
278
279         now = timeval_current();
280         *to_ret = timeval_until(&now, &event_ctx->timed_events->when);
281
282         DEBUG(10, ("timed_events_timeout: %d/%d\n", (int)to_ret->tv_sec,
283                 (int)to_ret->tv_usec));
284
285         return to_ret;
286 }
287
288 struct event_context *event_context_init(TALLOC_CTX *mem_ctx)
289 {
290         return TALLOC_ZERO_P(NULL, struct event_context);
291 }
292
293 int set_event_dispatch_time(struct event_context *event_ctx,
294                             const char *event_name, struct timeval when)
295 {
296         int num_events = 0;
297         struct timed_event *te;
298
299         for (te = event_ctx->timed_events; te; te = te->next) {
300                 if (strcmp(event_name, te->event_name) == 0) {
301                         te->when = when;
302                         num_events++;
303                 }
304         }
305         return num_events;
306 }
307
308 /* Returns 1 if event was found and cancelled, 0 otherwise. */
309
310 int cancel_named_event(struct event_context *event_ctx,
311                        const char *event_name)
312 {
313         struct timed_event *te;
314
315         for (te = event_ctx->timed_events; te; te = te->next) {
316                 if (strcmp(event_name, te->event_name) == 0) {
317                         TALLOC_FREE(te);
318                         return 1;
319                 }
320         }
321         return 0;
322 }