r20912: Ensure the list always remains sorted even when
[metze/samba/wip.git] / source3 / 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  Add te by time.
73 ****************************************************************************/
74
75 static void add_event_by_time(struct timed_event *te)
76 {
77         struct timed_event *last_te, *cur_te;
78
79         /* Keep the list ordered by time. We must preserve this. */
80         last_te = NULL;
81         for (cur_te = timed_events; cur_te; cur_te = cur_te->next) {
82                 /* if the new event comes before the current one break */
83                 if (!timeval_is_zero(&cur_te->when) &&
84                                 timeval_compare(&te->when, &cur_te->when) < 0) {
85                         break;
86                 }
87                 last_te = cur_te;
88         }
89
90         DLIST_ADD_AFTER(timed_events, te, last_te);
91 }
92
93 /****************************************************************************
94  Schedule a function for future calling, cancel with TALLOC_FREE().
95  It's the responsibility of the handler to call TALLOC_FREE() on the event
96  handed to it.
97 ****************************************************************************/
98
99 struct timed_event *event_add_timed(struct event_context *event_ctx,
100                                 TALLOC_CTX *mem_ctx,
101                                 struct timeval when,
102                                 const char *event_name,
103                                 void (*handler)(struct event_context *event_ctx,
104                                                 struct timed_event *te,
105                                                 const struct timeval *now,
106                                                 void *private_data),
107                                 void *private_data)
108 {
109         struct timed_event *te;
110
111         te = TALLOC_P(mem_ctx, struct timed_event);
112         if (te == NULL) {
113                 DEBUG(0, ("talloc failed\n"));
114                 return NULL;
115         }
116
117         te->event_ctx = event_ctx;
118         te->when = when;
119         te->event_name = event_name;
120         te->handler = handler;
121         te->private_data = private_data;
122
123         add_event_by_time(te);
124
125         talloc_set_destructor(te, timed_event_destructor);
126
127         DEBUG(10, ("Added timed event \"%s\": %lx\n", event_name,
128                         (unsigned long)te));
129         return te;
130 }
131
132 static int fd_event_destructor(struct fd_event *fde)
133 {
134         struct event_context *event_ctx = fde->event_ctx;
135
136         DLIST_REMOVE(event_ctx->fd_events, fde);
137         return 0;
138 }
139
140 struct fd_event *event_add_fd(struct event_context *event_ctx,
141                               TALLOC_CTX *mem_ctx,
142                               int fd, uint16_t flags,
143                               void (*handler)(struct event_context *event_ctx,
144                                               struct fd_event *event,
145                                               uint16 flags,
146                                               void *private_data),
147                               void *private_data)
148 {
149         struct fd_event *fde;
150
151         if (!(fde = TALLOC_P(mem_ctx, struct fd_event))) {
152                 return NULL;
153         }
154
155         fde->event_ctx = event_ctx;
156         fde->fd = fd;
157         fde->flags = flags;
158         fde->handler = handler;
159         fde->private_data = private_data;
160
161         DLIST_ADD(event_ctx->fd_events, fde);
162
163         talloc_set_destructor(fde, fd_event_destructor);
164         return fde;
165 }
166
167 void event_fd_set_writeable(struct fd_event *fde)
168 {
169         fde->flags |= EVENT_FD_WRITE;
170 }
171
172 void event_fd_set_not_writeable(struct fd_event *fde)
173 {
174         fde->flags &= ~EVENT_FD_WRITE;
175 }
176
177 void event_fd_set_readable(struct fd_event *fde)
178 {
179         fde->flags |= EVENT_FD_READ;
180 }
181
182 void event_fd_set_not_readable(struct fd_event *fde)
183 {
184         fde->flags &= ~EVENT_FD_READ;
185 }
186
187 void event_add_to_select_args(struct event_context *event_ctx,
188                               const struct timeval *now,
189                               fd_set *read_fds, fd_set *write_fds,
190                               struct timeval *timeout, int *maxfd)
191 {
192         struct fd_event *fde;
193         struct timeval diff;
194
195         for (fde = event_ctx->fd_events; fde; fde = fde->next) {
196                 if (fde->flags & EVENT_FD_READ) {
197                         FD_SET(fde->fd, read_fds);
198                 }
199                 if (fde->flags & EVENT_FD_WRITE) {
200                         FD_SET(fde->fd, write_fds);
201                 }
202
203                 if ((fde->flags & (EVENT_FD_READ|EVENT_FD_WRITE))
204                     && (fde->fd > *maxfd)) {
205                         *maxfd = fde->fd;
206                 }
207         }
208
209         if (event_ctx->timed_events == NULL) {
210                 return;
211         }
212
213         diff = timeval_until(now, &event_ctx->timed_events->when);
214         *timeout = timeval_min(timeout, &diff);
215 }
216
217 BOOL run_events(struct event_context *event_ctx,
218                 int selrtn, fd_set *read_fds, fd_set *write_fds)
219 {
220         BOOL fired = False;
221         struct fd_event *fde, *next;
222
223         /* Run all events that are pending, not just one (as we
224            did previously. */
225
226         while (event_ctx->timed_events) {
227                 struct timeval now;
228                 GetTimeOfDay(&now);
229
230                 if (timeval_compare(
231                             &now, &event_ctx->timed_events->when) < 0) {
232                         /* Nothing to do yet */
233                         DEBUG(11, ("run_events: Nothing to do\n"));
234                         break;
235                 }
236
237                 DEBUG(10, ("Running event \"%s\" %lx\n",
238                            event_ctx->timed_events->event_name,
239                            (unsigned long)event_ctx->timed_events));
240
241                 event_ctx->timed_events->handler(
242                         event_ctx,
243                         event_ctx->timed_events, &now,
244                         event_ctx->timed_events->private_data);
245
246                 fired = True;
247         }
248
249         if (fired) {
250                 /*
251                  * We might have changed the socket status during the timed
252                  * events, return to run select again.
253                  */
254                 return True;
255         }
256
257         if (selrtn == 0) {
258                 /*
259                  * No fd ready
260                  */
261                 return fired;
262         }
263
264         for (fde = event_ctx->fd_events; fde; fde = next) {
265                 uint16 flags = 0;
266
267                 next = fde->next;
268                 if (FD_ISSET(fde->fd, read_fds)) flags |= EVENT_FD_READ;
269                 if (FD_ISSET(fde->fd, write_fds)) flags |= EVENT_FD_WRITE;
270
271                 if (flags) {
272                         fde->handler(event_ctx, fde, flags, fde->private_data);
273                         fired = True;
274                 }
275         }
276
277         return fired;
278 }
279
280
281 struct timeval *get_timed_events_timeout(struct event_context *event_ctx,
282                                          struct timeval *to_ret)
283 {
284         struct timeval now;
285
286         if (event_ctx->timed_events == NULL) {
287                 return NULL;
288         }
289
290         now = timeval_current();
291         *to_ret = timeval_until(&now, &event_ctx->timed_events->when);
292
293         DEBUG(10, ("timed_events_timeout: %d/%d\n", (int)to_ret->tv_sec,
294                 (int)to_ret->tv_usec));
295
296         return to_ret;
297 }
298
299 struct event_context *event_context_init(TALLOC_CTX *mem_ctx)
300 {
301         return TALLOC_ZERO_P(NULL, struct event_context);
302 }
303
304 int set_event_dispatch_time(struct event_context *event_ctx,
305                             const char *event_name, struct timeval when)
306 {
307         struct timed_event *te;
308
309         for (te = event_ctx->timed_events; te; te = te->next) {
310                 if (strcmp(event_name, te->event_name) == 0) {
311                         DLIST_REMOVE(timed_events, te);
312                         te->when = when;
313                         add_event_by_time(te);
314                         return 1;
315                 }
316         }
317         return 0;
318 }
319
320 /* Returns 1 if event was found and cancelled, 0 otherwise. */
321
322 int cancel_named_event(struct event_context *event_ctx,
323                        const char *event_name)
324 {
325         struct timed_event *te;
326
327         for (te = event_ctx->timed_events; te; te = te->next) {
328                 if (strcmp(event_name, te->event_name) == 0) {
329                         TALLOC_FREE(te);
330                         return 1;
331                 }
332         }
333         return 0;
334 }