1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "backtrace-string.h"
6 #include "llist.h"
7 #include "time-util.h"
8 #include "istream-private.h"
9 #include "ioloop-private.h"
10
11 #include <unistd.h>
12
13 /* Dovecot attempts to detect also when time suddenly jumps forwards.
14 This is done by getting the minimum timeout wait in epoll() (or similar)
15 and then seeing if the current time after epoll() is past the timeout.
16 This can't be very exact, so likely the difference is always at least
17 1 microsecond. In high load situations it can be somewhat higher.
18 Dovecot generally doesn't have very important short timeouts, so to avoid
19 logging many warnings about this, use a rather high value. */
20 #define IOLOOP_TIME_MOVED_FORWARDS_MIN_USECS (100000)
21
22 time_t ioloop_time = 0;
23 struct timeval ioloop_timeval;
24 struct ioloop *current_ioloop = NULL;
25 uint64_t ioloop_global_wait_usecs = 0;
26
27 static ARRAY(io_switch_callback_t *) io_switch_callbacks = ARRAY_INIT;
28 static ARRAY(io_destroy_callback_t *) io_destroy_callbacks = ARRAY_INIT;
29 static bool panic_on_leak = FALSE, panic_on_leak_set = FALSE;
30
31 static time_t data_stack_last_free_unused = 0;
32
io_loop_initialize_handler(struct ioloop * ioloop)33 static void io_loop_initialize_handler(struct ioloop *ioloop)
34 {
35 unsigned int initial_fd_count;
36
37 initial_fd_count = ioloop->max_fd_count > 0 &&
38 ioloop->max_fd_count < IOLOOP_INITIAL_FD_COUNT ?
39 ioloop->max_fd_count : IOLOOP_INITIAL_FD_COUNT;
40 io_loop_handler_init(ioloop, initial_fd_count);
41 }
42
43 static struct io_file *
io_add_file(struct ioloop * ioloop,int fd,enum io_condition condition,const char * source_filename,unsigned int source_linenum,io_callback_t * callback,void * context)44 io_add_file(struct ioloop *ioloop, int fd, enum io_condition condition,
45 const char *source_filename,
46 unsigned int source_linenum,
47 io_callback_t *callback, void *context)
48 {
49 struct io_file *io;
50
51 i_assert(callback != NULL);
52 i_assert((condition & IO_NOTIFY) == 0);
53
54 io = i_new(struct io_file, 1);
55 io->io.condition = condition;
56 io->io.callback = callback;
57 io->io.context = context;
58 io->io.ioloop = ioloop;
59 io->io.source_filename = source_filename;
60 io->io.source_linenum = source_linenum;
61 io->refcount = 1;
62 io->fd = fd;
63
64 if (io->io.ioloop->cur_ctx != NULL) {
65 io->io.ctx = io->io.ioloop->cur_ctx;
66 io_loop_context_ref(io->io.ctx);
67 }
68
69 if (io->io.ioloop->handler_context == NULL)
70 io_loop_initialize_handler(io->io.ioloop);
71 if (fd != -1)
72 io_loop_handle_add(io);
73 else {
74 /* we're adding an istream whose only way to get notified
75 is to call i_stream_set_input_pending() */
76 }
77
78 if (io->io.ioloop->io_files != NULL) {
79 io->io.ioloop->io_files->prev = io;
80 io->next = io->io.ioloop->io_files;
81 }
82 io->io.ioloop->io_files = io;
83 return io;
84 }
85
86 #undef io_add_to
io_add_to(struct ioloop * ioloop,int fd,enum io_condition condition,const char * source_filename,unsigned int source_linenum,io_callback_t * callback,void * context)87 struct io *io_add_to(struct ioloop *ioloop, int fd, enum io_condition condition,
88 const char *source_filename, unsigned int source_linenum,
89 io_callback_t *callback, void *context)
90 {
91 struct io_file *io;
92
93 i_assert(fd >= 0);
94 io = io_add_file(ioloop, fd, condition,
95 source_filename, source_linenum,
96 callback, context);
97 return &io->io;
98 }
99
100 #undef io_add
io_add(int fd,enum io_condition condition,const char * source_filename,unsigned int source_linenum,io_callback_t * callback,void * context)101 struct io *io_add(int fd, enum io_condition condition,
102 const char *source_filename,
103 unsigned int source_linenum,
104 io_callback_t *callback, void *context)
105 {
106 return io_add_to(current_ioloop, fd, condition,
107 source_filename, source_linenum,
108 callback, context);
109 }
110
111 #undef io_add_istream_to
io_add_istream_to(struct ioloop * ioloop,struct istream * input,const char * source_filename,unsigned int source_linenum,io_callback_t * callback,void * context)112 struct io *io_add_istream_to(struct ioloop *ioloop, struct istream *input,
113 const char *source_filename,
114 unsigned int source_linenum,
115 io_callback_t *callback, void *context)
116 {
117 struct io_file *io;
118
119 io = io_add_file(ioloop, i_stream_get_fd(input), IO_READ,
120 source_filename, source_linenum, callback, context);
121 io->istream = input;
122 i_stream_ref(io->istream);
123 i_stream_set_io(io->istream, &io->io);
124 return &io->io;
125 }
126
127 #undef io_add_istream
io_add_istream(struct istream * input,const char * source_filename,unsigned int source_linenum,io_callback_t * callback,void * context)128 struct io *io_add_istream(struct istream *input, const char *source_filename,
129 unsigned int source_linenum,
130 io_callback_t *callback, void *context)
131 {
132 return io_add_istream_to(current_ioloop, input,
133 source_filename, source_linenum,
134 callback, context);
135 }
136
io_file_unlink(struct io_file * io)137 static void io_file_unlink(struct io_file *io)
138 {
139 if (io->prev != NULL)
140 io->prev->next = io->next;
141 else
142 io->io.ioloop->io_files = io->next;
143
144 if (io->next != NULL)
145 io->next->prev = io->prev;
146
147 /* if we got here from an I/O handler callback, make sure we
148 don't try to handle this one next. */
149 if (io->io.ioloop->next_io_file == io)
150 io->io.ioloop->next_io_file = io->next;
151 }
152
io_remove_full(struct io ** _io,bool closed)153 static void io_remove_full(struct io **_io, bool closed)
154 {
155 struct io *io = *_io;
156
157 i_assert(io->callback != NULL);
158
159 *_io = NULL;
160
161 /* make sure the callback doesn't get called anymore.
162 kqueue code relies on this. */
163 io->callback = NULL;
164
165 if (io->pending) {
166 i_assert(io->ioloop->io_pending_count > 0);
167 io->ioloop->io_pending_count--;
168 }
169
170 if (io->ctx != NULL)
171 io_loop_context_unref(&io->ctx);
172
173 if ((io->condition & IO_NOTIFY) != 0)
174 io_loop_notify_remove(io);
175 else {
176 struct io_file *io_file = (struct io_file *)io;
177 struct istream *istream = io_file->istream;
178
179 if (istream != NULL) {
180 /* remove io before it's freed */
181 i_stream_unset_io(istream, io);
182 }
183
184 io_file_unlink(io_file);
185 if (io_file->fd != -1)
186 io_loop_handle_remove(io_file, closed);
187 else
188 i_free(io);
189
190 /* remove io from the ioloop before unreferencing the istream,
191 because a destroyed istream may automatically close the
192 fd. */
193 i_stream_unref(&istream);
194 }
195 }
196
io_remove(struct io ** io)197 void io_remove(struct io **io)
198 {
199 if (*io == NULL)
200 return;
201
202 io_remove_full(io, FALSE);
203 }
204
io_remove_closed(struct io ** io)205 void io_remove_closed(struct io **io)
206 {
207 if (*io == NULL)
208 return;
209
210 i_assert(((*io)->condition & IO_NOTIFY) == 0);
211
212 io_remove_full(io, TRUE);
213 }
214
io_set_pending(struct io * io)215 void io_set_pending(struct io *io)
216 {
217 i_assert((io->condition & IO_NOTIFY) == 0);
218
219 if (!io->pending) {
220 io->pending = TRUE;
221 io->ioloop->io_pending_count++;
222 }
223 }
224
io_is_pending(struct io * io)225 bool io_is_pending(struct io *io)
226 {
227 return io->pending;
228 }
229
io_set_never_wait_alone(struct io * io,bool set)230 void io_set_never_wait_alone(struct io *io, bool set)
231 {
232 io->never_wait_alone = set;
233 }
234
timeout_update_next(struct timeout * timeout,struct timeval * tv_now)235 static void timeout_update_next(struct timeout *timeout, struct timeval *tv_now)
236 {
237 if (tv_now == NULL)
238 i_gettimeofday(&timeout->next_run);
239 else {
240 timeout->next_run.tv_sec = tv_now->tv_sec;
241 timeout->next_run.tv_usec = tv_now->tv_usec;
242 }
243
244 /* we don't want microsecond accuracy or this function will be
245 called all the time - millisecond is more than enough */
246 timeout->next_run.tv_usec -= timeout->next_run.tv_usec % 1000;
247
248 timeout->next_run.tv_sec += timeout->msecs/1000;
249 timeout->next_run.tv_usec += (timeout->msecs%1000)*1000;
250
251 if (timeout->next_run.tv_usec >= 1000000) {
252 timeout->next_run.tv_sec++;
253 timeout->next_run.tv_usec -= 1000000;
254 }
255 }
256
257 static struct timeout *
timeout_add_common(struct ioloop * ioloop,const char * source_filename,unsigned int source_linenum,timeout_callback_t * callback,void * context)258 timeout_add_common(struct ioloop *ioloop, const char *source_filename,
259 unsigned int source_linenum,
260 timeout_callback_t *callback, void *context)
261 {
262 struct timeout *timeout;
263
264 timeout = i_new(struct timeout, 1);
265 timeout->item.idx = UINT_MAX;
266 timeout->source_filename = source_filename;
267 timeout->source_linenum = source_linenum;
268 timeout->ioloop = ioloop;
269
270 timeout->callback = callback;
271 timeout->context = context;
272
273 if (timeout->ioloop->cur_ctx != NULL) {
274 timeout->ctx = timeout->ioloop->cur_ctx;
275 io_loop_context_ref(timeout->ctx);
276 }
277
278 return timeout;
279 }
280
281 #undef timeout_add_to
timeout_add_to(struct ioloop * ioloop,unsigned int msecs,const char * source_filename,unsigned int source_linenum,timeout_callback_t * callback,void * context)282 struct timeout *timeout_add_to(struct ioloop *ioloop, unsigned int msecs,
283 const char *source_filename,
284 unsigned int source_linenum,
285 timeout_callback_t *callback, void *context)
286 {
287 struct timeout *timeout;
288
289 timeout = timeout_add_common(ioloop, source_filename, source_linenum,
290 callback, context);
291 timeout->msecs = msecs;
292
293 if (msecs > 0) {
294 /* start this timeout in the next run cycle */
295 array_push_back(&timeout->ioloop->timeouts_new, &timeout);
296 } else {
297 /* Trigger zero timeouts as soon as possible. When ioloop is
298 running, refresh the timestamp to prevent infinite loops
299 in case a timeout callback keeps recreating the 0-timeout. */
300 timeout_update_next(timeout, timeout->ioloop->running ?
301 NULL : &ioloop_timeval);
302 priorityq_add(timeout->ioloop->timeouts, &timeout->item);
303 }
304 return timeout;
305 }
306
307 #undef timeout_add
timeout_add(unsigned int msecs,const char * source_filename,unsigned int source_linenum,timeout_callback_t * callback,void * context)308 struct timeout *timeout_add(unsigned int msecs, const char *source_filename,
309 unsigned int source_linenum,
310 timeout_callback_t *callback, void *context)
311 {
312 return timeout_add_to(current_ioloop, msecs,
313 source_filename, source_linenum,
314 callback, context);
315 }
316
317 #undef timeout_add_short_to
318 struct timeout *
timeout_add_short_to(struct ioloop * ioloop,unsigned int msecs,const char * source_filename,unsigned int source_linenum,timeout_callback_t * callback,void * context)319 timeout_add_short_to(struct ioloop *ioloop, unsigned int msecs,
320 const char *source_filename, unsigned int source_linenum,
321 timeout_callback_t *callback, void *context)
322 {
323 return timeout_add_to(ioloop, msecs,
324 source_filename, source_linenum,
325 callback, context);
326 }
327
328 #undef timeout_add_short
329 struct timeout *
timeout_add_short(unsigned int msecs,const char * source_filename,unsigned int source_linenum,timeout_callback_t * callback,void * context)330 timeout_add_short(unsigned int msecs, const char *source_filename,
331 unsigned int source_linenum,
332 timeout_callback_t *callback, void *context)
333 {
334 return timeout_add(msecs, source_filename, source_linenum,
335 callback, context);
336 }
337
338 #undef timeout_add_absolute_to
339 struct timeout *
timeout_add_absolute_to(struct ioloop * ioloop,const struct timeval * time,const char * source_filename,unsigned int source_linenum,timeout_callback_t * callback,void * context)340 timeout_add_absolute_to(struct ioloop *ioloop, const struct timeval *time,
341 const char *source_filename,
342 unsigned int source_linenum,
343 timeout_callback_t *callback, void *context)
344 {
345 struct timeout *timeout;
346
347 timeout = timeout_add_common(ioloop, source_filename, source_linenum,
348 callback, context);
349 timeout->one_shot = TRUE;
350 timeout->next_run = *time;
351
352 priorityq_add(timeout->ioloop->timeouts, &timeout->item);
353 return timeout;
354 }
355
356 #undef timeout_add_absolute
357 struct timeout *
timeout_add_absolute(const struct timeval * time,const char * source_filename,unsigned int source_linenum,timeout_callback_t * callback,void * context)358 timeout_add_absolute(const struct timeval *time,
359 const char *source_filename,
360 unsigned int source_linenum,
361 timeout_callback_t *callback, void *context)
362 {
363 return timeout_add_absolute_to(current_ioloop, time,
364 source_filename, source_linenum,
365 callback, context);
366 }
367
368 static struct timeout *
timeout_copy(const struct timeout * old_to,struct ioloop * ioloop)369 timeout_copy(const struct timeout *old_to, struct ioloop *ioloop)
370 {
371 struct timeout *new_to;
372
373 new_to = timeout_add_common(ioloop,
374 old_to->source_filename, old_to->source_linenum,
375 old_to->callback, old_to->context);
376 new_to->one_shot = old_to->one_shot;
377 new_to->msecs = old_to->msecs;
378 new_to->next_run = old_to->next_run;
379
380 if (old_to->item.idx != UINT_MAX)
381 priorityq_add(new_to->ioloop->timeouts, &new_to->item);
382 else if (!new_to->one_shot) {
383 i_assert(new_to->msecs > 0);
384 array_push_back(&new_to->ioloop->timeouts_new, &new_to);
385 }
386
387 return new_to;
388 }
389
timeout_free(struct timeout * timeout)390 static void timeout_free(struct timeout *timeout)
391 {
392 if (timeout->ctx != NULL)
393 io_loop_context_unref(&timeout->ctx);
394 i_free(timeout);
395 }
396
timeout_remove(struct timeout ** _timeout)397 void timeout_remove(struct timeout **_timeout)
398 {
399 struct timeout *timeout = *_timeout;
400 struct ioloop *ioloop;
401
402 if (timeout == NULL)
403 return;
404
405 ioloop = timeout->ioloop;
406
407 *_timeout = NULL;
408 if (timeout->item.idx != UINT_MAX)
409 priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
410 else if (!timeout->one_shot && timeout->msecs > 0) {
411 struct timeout *const *to_idx;
412 array_foreach(&ioloop->timeouts_new, to_idx) {
413 if (*to_idx == timeout) {
414 array_delete(&ioloop->timeouts_new,
415 array_foreach_idx(&ioloop->timeouts_new, to_idx), 1);
416 break;
417 }
418 }
419 }
420 timeout_free(timeout);
421 }
422
423 static void ATTR_NULL(2)
timeout_reset_timeval(struct timeout * timeout,struct timeval * tv_now)424 timeout_reset_timeval(struct timeout *timeout, struct timeval *tv_now)
425 {
426 if (timeout->item.idx == UINT_MAX)
427 return;
428
429 timeout_update_next(timeout, tv_now);
430 /* If we came here from io_loop_handle_timeouts_real(), next_run must
431 be larger than tv_now or it can go to infinite loop. This would
432 mainly happen with 0 ms timeouts. Avoid this by making sure
433 next_run is at least 1 us higher than tv_now.
434
435 Note that some callers (like master process's process_min_avail
436 preforking timeout) really do want the 0 ms timeout to trigger
437 multiple times as rapidly as it can (but in separate ioloop runs).
438 So don't increase it more than by 1 us. */
439 if (tv_now != NULL && timeval_cmp(&timeout->next_run, tv_now) <= 0) {
440 timeout->next_run = *tv_now;
441 timeval_add_usecs(&timeout->next_run, 1);
442 }
443 priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
444 priorityq_add(timeout->ioloop->timeouts, &timeout->item);
445 }
446
timeout_reset(struct timeout * timeout)447 void timeout_reset(struct timeout *timeout)
448 {
449 i_assert(!timeout->one_shot);
450 timeout_reset_timeval(timeout, NULL);
451 }
452
timeout_get_wait_time(struct timeout * timeout,struct timeval * tv_r,struct timeval * tv_now,bool in_timeout_loop)453 static int timeout_get_wait_time(struct timeout *timeout, struct timeval *tv_r,
454 struct timeval *tv_now, bool in_timeout_loop)
455 {
456 int ret;
457
458 if (tv_now->tv_sec == 0)
459 i_gettimeofday(tv_now);
460 tv_r->tv_sec = tv_now->tv_sec;
461 tv_r->tv_usec = tv_now->tv_usec;
462
463 i_assert(tv_r->tv_sec > 0);
464 i_assert(timeout->next_run.tv_sec > 0);
465
466 tv_r->tv_sec = timeout->next_run.tv_sec - tv_r->tv_sec;
467 tv_r->tv_usec = timeout->next_run.tv_usec - tv_r->tv_usec;
468 if (tv_r->tv_usec < 0) {
469 tv_r->tv_sec--;
470 tv_r->tv_usec += 1000000;
471 }
472
473 if (tv_r->tv_sec < 0) {
474 /* The timeout should have been called already */
475 tv_r->tv_sec = 0;
476 tv_r->tv_usec = 0;
477 return 0;
478 }
479 if (tv_r->tv_sec == 0 && tv_r->tv_usec == 1 && !in_timeout_loop) {
480 /* Possibly 0 ms timeout. Don't wait for a full millisecond
481 for it to trigger. */
482 tv_r->tv_usec = 0;
483 return 0;
484 }
485 if (tv_r->tv_sec > INT_MAX/1000-1)
486 tv_r->tv_sec = INT_MAX/1000-1;
487
488 /* round wait times up to next millisecond */
489 ret = tv_r->tv_sec * 1000 + (tv_r->tv_usec + 999) / 1000;
490 i_assert(ret >= 0 && tv_r->tv_sec >= 0 && tv_r->tv_usec >= 0);
491 return ret;
492 }
493
io_loop_get_wait_time(struct ioloop * ioloop,struct timeval * tv_r)494 static int io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
495 {
496 struct timeval tv_now;
497 struct priorityq_item *item;
498 struct timeout *timeout;
499 int msecs;
500
501 item = priorityq_peek(ioloop->timeouts);
502 timeout = (struct timeout *)item;
503
504 /* we need to see if there are pending IO waiting,
505 if there is, we set msecs = 0 to ensure they are
506 processed without delay */
507 if (timeout == NULL && ioloop->io_pending_count == 0) {
508 /* no timeouts. use INT_MAX msecs for timeval and
509 return -1 for poll/epoll infinity. */
510 tv_r->tv_sec = INT_MAX / 1000;
511 tv_r->tv_usec = 0;
512 ioloop->next_max_time.tv_sec = (1ULL << (TIME_T_MAX_BITS-1)) - 1;
513 ioloop->next_max_time.tv_usec = 0;
514 return -1;
515 }
516
517 if (ioloop->io_pending_count > 0) {
518 i_gettimeofday(&tv_now);
519 msecs = 0;
520 tv_r->tv_sec = 0;
521 tv_r->tv_usec = 0;
522 } else {
523 tv_now.tv_sec = 0;
524 msecs = timeout_get_wait_time(timeout, tv_r, &tv_now, FALSE);
525 }
526 ioloop->next_max_time = tv_now;
527 timeval_add_msecs(&ioloop->next_max_time, msecs);
528
529 /* update ioloop_timeval - this is meant for io_loop_handle_timeouts()'s
530 ioloop_wait_usecs calculation. normally after this we go to the
531 ioloop and after that we update ioloop_timeval immediately again. */
532 ioloop_timeval = tv_now;
533 ioloop_time = tv_now.tv_sec;
534 i_assert(msecs == 0 || timeout->msecs > 0 || timeout->one_shot);
535 return msecs;
536 }
537
io_loop_have_waitable_io_files(struct ioloop * ioloop)538 static bool io_loop_have_waitable_io_files(struct ioloop *ioloop)
539 {
540 struct io_file *io;
541
542 for (io = ioloop->io_files; io != NULL; io = io->next) {
543 if (io->io.callback != NULL && !io->io.never_wait_alone)
544 return TRUE;
545 }
546 return FALSE;
547 }
548
io_loop_run_get_wait_time(struct ioloop * ioloop,struct timeval * tv_r)549 int io_loop_run_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r)
550 {
551 int msecs = io_loop_get_wait_time(ioloop, tv_r);
552 if (msecs < 0 && !io_loop_have_waitable_io_files(ioloop))
553 i_panic("BUG: No IOs or timeouts set. Not waiting for infinity.");
554 return msecs;
555 }
556
timeout_cmp(const void * p1,const void * p2)557 static int timeout_cmp(const void *p1, const void *p2)
558 {
559 const struct timeout *to1 = p1, *to2 = p2;
560
561 return timeval_cmp(&to1->next_run, &to2->next_run);
562 }
563
564 static void
io_loop_default_time_moved(const struct timeval * old_time,const struct timeval * new_time)565 io_loop_default_time_moved(const struct timeval *old_time,
566 const struct timeval *new_time)
567 {
568 long long diff = timeval_diff_usecs(old_time, new_time);
569 if (diff > 0) {
570 i_warning("Time moved backwards by %lld.%06lld seconds.",
571 diff / 1000000, diff % 1000000);
572 }
573 }
574
io_loop_timeouts_start_new(struct ioloop * ioloop)575 static void io_loop_timeouts_start_new(struct ioloop *ioloop)
576 {
577 struct timeout *timeout;
578
579 if (array_count(&ioloop->timeouts_new) == 0)
580 return;
581
582 io_loop_time_refresh();
583
584 array_foreach_elem(&ioloop->timeouts_new, timeout) {
585 i_assert(timeout->next_run.tv_sec == 0 &&
586 timeout->next_run.tv_usec == 0);
587 i_assert(!timeout->one_shot);
588 i_assert(timeout->msecs > 0);
589 timeout_update_next(timeout, &ioloop_timeval);
590 priorityq_add(ioloop->timeouts, &timeout->item);
591 }
592 array_clear(&ioloop->timeouts_new);
593 }
594
io_loop_timeouts_update(struct ioloop * ioloop,long long diff_usecs)595 static void io_loop_timeouts_update(struct ioloop *ioloop, long long diff_usecs)
596 {
597 struct priorityq_item *const *items;
598 unsigned int i, count;
599
600 count = priorityq_count(ioloop->timeouts);
601 items = priorityq_items(ioloop->timeouts);
602 for (i = 0; i < count; i++) {
603 struct timeout *to = (struct timeout *)items[i];
604
605 if (diff_usecs > 0)
606 timeval_add_usecs(&to->next_run, diff_usecs);
607 else
608 timeval_sub_usecs(&to->next_run, -diff_usecs);
609 }
610 }
611
io_loops_timeouts_update(long long diff_usecs)612 static void io_loops_timeouts_update(long long diff_usecs)
613 {
614 struct ioloop *ioloop;
615
616 for (ioloop = current_ioloop; ioloop != NULL; ioloop = ioloop->prev)
617 io_loop_timeouts_update(ioloop, diff_usecs);
618 }
619
ioloop_add_wait_time(struct ioloop * ioloop)620 static void ioloop_add_wait_time(struct ioloop *ioloop)
621 {
622 struct io_wait_timer *timer;
623 long long diff;
624
625 diff = timeval_diff_usecs(&ioloop_timeval, &ioloop->wait_started);
626 if (diff < 0) {
627 /* time moved backwards */
628 diff = 0;
629 }
630
631 ioloop->ioloop_wait_usecs += diff;
632 ioloop_global_wait_usecs += diff;
633
634 for (timer = ioloop->wait_timers; timer != NULL; timer = timer->next)
635 timer->usecs += diff;
636 }
637
io_loop_handle_timeouts_real(struct ioloop * ioloop)638 static void io_loop_handle_timeouts_real(struct ioloop *ioloop)
639 {
640 struct priorityq_item *item;
641 struct timeval tv_old, tv, tv_call;
642 long long diff_usecs;
643 data_stack_frame_t t_id;
644
645 tv_old = ioloop_timeval;
646 i_gettimeofday(&ioloop_timeval);
647
648 diff_usecs = timeval_diff_usecs(&ioloop_timeval, &tv_old);
649 if (unlikely(diff_usecs < 0)) {
650 /* time moved backwards */
651 io_loops_timeouts_update(diff_usecs);
652 ioloop->time_moved_callback(&tv_old, &ioloop_timeval);
653 i_assert(ioloop == current_ioloop);
654 /* the callback may have slept, so check the time again. */
655 i_gettimeofday(&ioloop_timeval);
656 } else {
657 diff_usecs = timeval_diff_usecs(&ioloop->next_max_time,
658 &ioloop_timeval);
659 if (unlikely(-diff_usecs >= IOLOOP_TIME_MOVED_FORWARDS_MIN_USECS)) {
660 io_loops_timeouts_update(-diff_usecs);
661 /* time moved forwards */
662 ioloop->time_moved_callback(&ioloop->next_max_time,
663 &ioloop_timeval);
664 i_assert(ioloop == current_ioloop);
665 }
666 ioloop_add_wait_time(ioloop);
667 }
668
669 ioloop_time = ioloop_timeval.tv_sec;
670 tv_call = ioloop_timeval;
671
672 while (ioloop->running &&
673 (item = priorityq_peek(ioloop->timeouts)) != NULL) {
674 struct timeout *timeout = (struct timeout *)item;
675
676 /* use tv_call to make sure we don't get to infinite loop in
677 case callbacks update ioloop_timeval. */
678 if (timeout_get_wait_time(timeout, &tv, &tv_call, TRUE) > 0)
679 break;
680
681 if (timeout->one_shot) {
682 /* remove timeout from queue */
683 priorityq_remove(timeout->ioloop->timeouts, &timeout->item);
684 } else {
685 /* update timeout's next_run and reposition it in the queue */
686 timeout_reset_timeval(timeout, &tv_call);
687 }
688
689 if (timeout->ctx != NULL)
690 io_loop_context_activate(timeout->ctx);
691 t_id = t_push_named("ioloop timeout handler %p",
692 (void *)timeout->callback);
693 timeout->callback(timeout->context);
694 if (!t_pop(&t_id)) {
695 i_panic("Leaked a t_pop() call in timeout handler %p",
696 (void *)timeout->callback);
697 }
698 if (ioloop->cur_ctx != NULL)
699 io_loop_context_deactivate(ioloop->cur_ctx);
700 i_assert(ioloop == current_ioloop);
701 }
702 }
703
io_loop_handle_timeouts(struct ioloop * ioloop)704 void io_loop_handle_timeouts(struct ioloop *ioloop)
705 {
706 T_BEGIN {
707 io_loop_handle_timeouts_real(ioloop);
708 } T_END;
709
710 /* Free the unused memory in data stack once per second. This way if
711 the data stack has grown excessively large temporarily, it won't
712 permanently waste memory. And if the data stack grows back to the
713 same large size, re-allocating it once per second doesn't cause
714 performance problems. */
715 if (data_stack_last_free_unused != ioloop_time) {
716 if (data_stack_last_free_unused != 0)
717 data_stack_free_unused();
718 data_stack_last_free_unused = ioloop_time;
719 }
720 }
721
io_loop_call_io(struct io * io)722 void io_loop_call_io(struct io *io)
723 {
724 struct ioloop *ioloop = io->ioloop;
725 data_stack_frame_t t_id;
726
727 if (io->pending) {
728 i_assert(ioloop->io_pending_count > 0);
729 ioloop->io_pending_count--;
730 io->pending = FALSE;
731 }
732
733 if (io->ctx != NULL)
734 io_loop_context_activate(io->ctx);
735 t_id = t_push_named("ioloop handler %p",
736 (void *)io->callback);
737 io->callback(io->context);
738 if (!t_pop(&t_id)) {
739 i_panic("Leaked a t_pop() call in I/O handler %p",
740 (void *)io->callback);
741 }
742 if (ioloop->cur_ctx != NULL)
743 io_loop_context_deactivate(ioloop->cur_ctx);
744 i_assert(ioloop == current_ioloop);
745 }
746
io_loop_run(struct ioloop * ioloop)747 void io_loop_run(struct ioloop *ioloop)
748 {
749 if (ioloop->handler_context == NULL)
750 io_loop_initialize_handler(ioloop);
751
752 if (ioloop->cur_ctx != NULL)
753 io_loop_context_deactivate(ioloop->cur_ctx);
754
755 /* recursive io_loop_run() isn't allowed for the same ioloop.
756 it can break backends. */
757 i_assert(!ioloop->iolooping);
758 ioloop->iolooping = TRUE;
759
760 ioloop->running = TRUE;
761 while (ioloop->running)
762 io_loop_handler_run(ioloop);
763 ioloop->iolooping = FALSE;
764 }
765
io_loop_call_pending(struct ioloop * ioloop)766 static void io_loop_call_pending(struct ioloop *ioloop)
767 {
768 struct io_file *io;
769
770 while (ioloop->io_pending_count > 0) {
771 io = ioloop->io_files;
772 do {
773 ioloop->next_io_file = io->next;
774 if (io->io.pending)
775 io_loop_call_io(&io->io);
776 if (ioloop->io_pending_count == 0)
777 break;
778 io = ioloop->next_io_file;
779 } while (io != NULL);
780 }
781 }
782
io_loop_handler_run(struct ioloop * ioloop)783 void io_loop_handler_run(struct ioloop *ioloop)
784 {
785 i_assert(ioloop == current_ioloop);
786
787 io_loop_timeouts_start_new(ioloop);
788 ioloop->wait_started = ioloop_timeval;
789 io_loop_handler_run_internal(ioloop);
790 io_loop_call_pending(ioloop);
791 if (ioloop->stop_after_run_loop)
792 io_loop_stop(ioloop);
793
794 i_assert(ioloop == current_ioloop);
795 }
796
io_loop_stop(struct ioloop * ioloop)797 void io_loop_stop(struct ioloop *ioloop)
798 {
799 ioloop->running = FALSE;
800 ioloop->stop_after_run_loop = FALSE;
801 }
802
io_loop_stop_delayed(struct ioloop * ioloop)803 void io_loop_stop_delayed(struct ioloop *ioloop)
804 {
805 ioloop->stop_after_run_loop = TRUE;
806 }
807
io_loop_set_running(struct ioloop * ioloop)808 void io_loop_set_running(struct ioloop *ioloop)
809 {
810 ioloop->running = TRUE;
811 }
812
io_loop_set_max_fd_count(struct ioloop * ioloop,unsigned int max_fds)813 void io_loop_set_max_fd_count(struct ioloop *ioloop, unsigned int max_fds)
814 {
815 ioloop->max_fd_count = max_fds;
816 }
817
io_loop_is_running(struct ioloop * ioloop)818 bool io_loop_is_running(struct ioloop *ioloop)
819 {
820 return ioloop->running;
821 }
822
io_loop_time_refresh(void)823 void io_loop_time_refresh(void)
824 {
825 i_gettimeofday(&ioloop_timeval);
826 ioloop_time = ioloop_timeval.tv_sec;
827 }
828
io_loop_create(void)829 struct ioloop *io_loop_create(void)
830 {
831 struct ioloop *ioloop;
832
833 if (!panic_on_leak_set) {
834 panic_on_leak_set = TRUE;
835 panic_on_leak = getenv("CORE_IO_LEAK") != NULL;
836 }
837
838 /* initialize time */
839 i_gettimeofday(&ioloop_timeval);
840 ioloop_time = ioloop_timeval.tv_sec;
841
842 ioloop = i_new(struct ioloop, 1);
843 ioloop->timeouts = priorityq_init(timeout_cmp, 32);
844 i_array_init(&ioloop->timeouts_new, 8);
845
846 ioloop->time_moved_callback = current_ioloop != NULL ?
847 current_ioloop->time_moved_callback :
848 io_loop_default_time_moved;
849
850 ioloop->prev = current_ioloop;
851 io_loop_set_current(ioloop);
852 return ioloop;
853 }
854
io_loop_destroy(struct ioloop ** _ioloop)855 void io_loop_destroy(struct ioloop **_ioloop)
856 {
857 struct ioloop *ioloop = *_ioloop;
858 struct timeout *to;
859 struct priorityq_item *item;
860 bool leaks = FALSE;
861
862 *_ioloop = NULL;
863
864 /* ->prev won't work unless loops are destroyed in create order */
865 i_assert(ioloop == current_ioloop);
866 if (array_is_created(&io_destroy_callbacks)) {
867 io_destroy_callback_t *callback;
868 array_foreach_elem(&io_destroy_callbacks, callback)
869 callback(current_ioloop);
870 }
871
872 io_loop_set_current(current_ioloop->prev);
873
874 if (ioloop->notify_handler_context != NULL)
875 io_loop_notify_handler_deinit(ioloop);
876
877 while (ioloop->io_files != NULL) {
878 struct io_file *io = ioloop->io_files;
879 struct io *_io = &io->io;
880 const char *error = t_strdup_printf(
881 "I/O leak: %p (%s:%u, fd %d)",
882 (void *)io->io.callback,
883 io->io.source_filename,
884 io->io.source_linenum, io->fd);
885
886 if (panic_on_leak)
887 i_panic("%s", error);
888 else
889 i_warning("%s", error);
890 io_remove(&_io);
891 leaks = TRUE;
892 }
893 i_assert(ioloop->io_pending_count == 0);
894
895 array_foreach_elem(&ioloop->timeouts_new, to) {
896 const char *error = t_strdup_printf(
897 "Timeout leak: %p (%s:%u)", (void *)to->callback,
898 to->source_filename,
899 to->source_linenum);
900
901 if (panic_on_leak)
902 i_panic("%s", error);
903 else
904 i_warning("%s", error);
905 timeout_free(to);
906 leaks = TRUE;
907 }
908 array_free(&ioloop->timeouts_new);
909
910 while ((item = priorityq_pop(ioloop->timeouts)) != NULL) {
911 struct timeout *to = (struct timeout *)item;
912 const char *error = t_strdup_printf(
913 "Timeout leak: %p (%s:%u)", (void *)to->callback,
914 to->source_filename,
915 to->source_linenum);
916
917 if (panic_on_leak)
918 i_panic("%s", error);
919 else
920 i_warning("%s", error);
921 timeout_free(to);
922 leaks = TRUE;
923 }
924 priorityq_deinit(&ioloop->timeouts);
925
926 while (ioloop->wait_timers != NULL) {
927 struct io_wait_timer *timer = ioloop->wait_timers;
928 const char *error = t_strdup_printf(
929 "IO wait timer leak: %s:%u",
930 timer->source_filename,
931 timer->source_linenum);
932
933 if (panic_on_leak)
934 i_panic("%s", error);
935 else
936 i_warning("%s", error);
937 io_wait_timer_remove(&timer);
938 leaks = TRUE;
939 }
940
941 if (leaks) {
942 const char *backtrace;
943 if (backtrace_get(&backtrace) == 0)
944 i_warning("Raw backtrace for leaks: %s", backtrace);
945 }
946
947 if (ioloop->handler_context != NULL)
948 io_loop_handler_deinit(ioloop);
949 if (ioloop->cur_ctx != NULL)
950 io_loop_context_unref(&ioloop->cur_ctx);
951 i_free(ioloop);
952 }
953
io_loop_set_time_moved_callback(struct ioloop * ioloop,io_loop_time_moved_callback_t * callback)954 void io_loop_set_time_moved_callback(struct ioloop *ioloop,
955 io_loop_time_moved_callback_t *callback)
956 {
957 ioloop->time_moved_callback = callback;
958 }
959
io_switch_callbacks_free(void)960 static void io_switch_callbacks_free(void)
961 {
962 array_free(&io_switch_callbacks);
963 }
964
io_destroy_callbacks_free(void)965 static void io_destroy_callbacks_free(void)
966 {
967 array_free(&io_destroy_callbacks);
968 }
969
io_loop_set_current(struct ioloop * ioloop)970 void io_loop_set_current(struct ioloop *ioloop)
971 {
972 io_switch_callback_t *callback;
973 struct ioloop *prev_ioloop = current_ioloop;
974
975 if (ioloop == current_ioloop)
976 return;
977
978 current_ioloop = ioloop;
979 if (array_is_created(&io_switch_callbacks)) {
980 array_foreach_elem(&io_switch_callbacks, callback)
981 callback(prev_ioloop);
982 }
983 }
984
io_loop_get_root(void)985 struct ioloop *io_loop_get_root(void)
986 {
987 struct ioloop *ioloop = current_ioloop;
988
989 while (ioloop->prev != NULL)
990 ioloop = ioloop->prev;
991 return ioloop;
992 }
993
io_loop_add_switch_callback(io_switch_callback_t * callback)994 void io_loop_add_switch_callback(io_switch_callback_t *callback)
995 {
996 if (!array_is_created(&io_switch_callbacks)) {
997 i_array_init(&io_switch_callbacks, 4);
998 lib_atexit_priority(io_switch_callbacks_free, LIB_ATEXIT_PRIORITY_LOW);
999 }
1000 array_push_back(&io_switch_callbacks, &callback);
1001 }
1002
io_loop_remove_switch_callback(io_switch_callback_t * callback)1003 void io_loop_remove_switch_callback(io_switch_callback_t *callback)
1004 {
1005 io_switch_callback_t *const *callbackp;
1006 unsigned int idx;
1007
1008 array_foreach(&io_switch_callbacks, callbackp) {
1009 if (*callbackp == callback) {
1010 idx = array_foreach_idx(&io_switch_callbacks, callbackp);
1011 array_delete(&io_switch_callbacks, idx, 1);
1012 return;
1013 }
1014 }
1015 i_unreached();
1016 }
1017
io_loop_add_destroy_callback(io_destroy_callback_t * callback)1018 void io_loop_add_destroy_callback(io_destroy_callback_t *callback)
1019 {
1020 if (!array_is_created(&io_destroy_callbacks)) {
1021 i_array_init(&io_destroy_callbacks, 4);
1022 lib_atexit_priority(io_destroy_callbacks_free, LIB_ATEXIT_PRIORITY_LOW);
1023 }
1024 array_push_back(&io_destroy_callbacks, &callback);
1025 }
1026
io_loop_remove_destroy_callback(io_destroy_callback_t * callback)1027 void io_loop_remove_destroy_callback(io_destroy_callback_t *callback)
1028 {
1029 io_destroy_callback_t *const *callbackp;
1030 unsigned int idx;
1031
1032 array_foreach(&io_destroy_callbacks, callbackp) {
1033 if (*callbackp == callback) {
1034 idx = array_foreach_idx(&io_destroy_callbacks, callbackp);
1035 array_delete(&io_destroy_callbacks, idx, 1);
1036 return;
1037 }
1038 }
1039 i_unreached();
1040 }
1041
io_loop_context_new(struct ioloop * ioloop)1042 struct ioloop_context *io_loop_context_new(struct ioloop *ioloop)
1043 {
1044 struct ioloop_context *ctx;
1045
1046 ctx = i_new(struct ioloop_context, 1);
1047 ctx->refcount = 1;
1048 ctx->ioloop = ioloop;
1049 i_array_init(&ctx->callbacks, 4);
1050 return ctx;
1051 }
1052
io_loop_context_ref(struct ioloop_context * ctx)1053 void io_loop_context_ref(struct ioloop_context *ctx)
1054 {
1055 i_assert(ctx->refcount > 0);
1056
1057 ctx->refcount++;
1058 }
1059
io_loop_context_unref(struct ioloop_context ** _ctx)1060 void io_loop_context_unref(struct ioloop_context **_ctx)
1061 {
1062 struct ioloop_context *ctx = *_ctx;
1063
1064 *_ctx = NULL;
1065
1066 i_assert(ctx->refcount > 0);
1067 if (--ctx->refcount > 0)
1068 return;
1069
1070 /* cur_ctx itself keeps a reference */
1071 i_assert(ctx->ioloop->cur_ctx != ctx);
1072
1073 array_free(&ctx->callbacks);
1074 i_free(ctx);
1075 }
1076
1077 #undef io_loop_context_add_callbacks
io_loop_context_add_callbacks(struct ioloop_context * ctx,io_callback_t * activate,io_callback_t * deactivate,void * context)1078 void io_loop_context_add_callbacks(struct ioloop_context *ctx,
1079 io_callback_t *activate,
1080 io_callback_t *deactivate, void *context)
1081 {
1082 struct ioloop_context_callback cb;
1083
1084 i_zero(&cb);
1085 cb.activate = activate;
1086 cb.deactivate = deactivate;
1087 cb.context = context;
1088
1089 array_push_back(&ctx->callbacks, &cb);
1090 }
1091
1092 #undef io_loop_context_remove_callbacks
io_loop_context_remove_callbacks(struct ioloop_context * ctx,io_callback_t * activate,io_callback_t * deactivate,void * context)1093 void io_loop_context_remove_callbacks(struct ioloop_context *ctx,
1094 io_callback_t *activate,
1095 io_callback_t *deactivate, void *context)
1096 {
1097 struct ioloop_context_callback *cb;
1098
1099 array_foreach_modifiable(&ctx->callbacks, cb) {
1100 if (cb->context == context &&
1101 cb->activate == activate && cb->deactivate == deactivate) {
1102 /* simply mark it as deleted, since we could get
1103 here from activate/deactivate loop */
1104 cb->activate = NULL;
1105 cb->deactivate = NULL;
1106 cb->context = NULL;
1107 return;
1108 }
1109 }
1110 i_panic("io_loop_context_remove_callbacks() context not found");
1111 }
1112
1113 static void
io_loop_context_remove_deleted_callbacks(struct ioloop_context * ctx)1114 io_loop_context_remove_deleted_callbacks(struct ioloop_context *ctx)
1115 {
1116 const struct ioloop_context_callback *cbs;
1117 unsigned int i, count;
1118
1119 cbs = array_get(&ctx->callbacks, &count);
1120 for (i = 0; i < count; ) {
1121 if (cbs[i].activate != NULL)
1122 i++;
1123 else {
1124 array_delete(&ctx->callbacks, i, 1);
1125 cbs = array_get(&ctx->callbacks, &count);
1126 }
1127 }
1128 }
1129
io_loop_context_activate(struct ioloop_context * ctx)1130 void io_loop_context_activate(struct ioloop_context *ctx)
1131 {
1132 struct ioloop_context_callback *cb;
1133
1134 i_assert(ctx->ioloop->cur_ctx == NULL);
1135
1136 ctx->ioloop->cur_ctx = ctx;
1137 io_loop_context_ref(ctx);
1138 array_foreach_modifiable(&ctx->callbacks, cb) {
1139 i_assert(!cb->activated);
1140 if (cb->activate != NULL)
1141 cb->activate(cb->context);
1142 cb->activated = TRUE;
1143 }
1144 }
1145
io_loop_context_deactivate(struct ioloop_context * ctx)1146 void io_loop_context_deactivate(struct ioloop_context *ctx)
1147 {
1148 struct ioloop_context_callback *cb;
1149
1150 i_assert(ctx->ioloop->cur_ctx == ctx);
1151
1152 array_foreach_modifiable(&ctx->callbacks, cb) {
1153 if (!cb->activated) {
1154 /* we just added this callback. don't deactivate it
1155 before it gets first activated. */
1156 } else {
1157 if (cb->deactivate != NULL)
1158 cb->deactivate(cb->context);
1159 cb->activated = FALSE;
1160 }
1161 }
1162 ctx->ioloop->cur_ctx = NULL;
1163 io_loop_context_remove_deleted_callbacks(ctx);
1164 io_loop_context_unref(&ctx);
1165 }
1166
io_loop_context_switch(struct ioloop_context * ctx)1167 void io_loop_context_switch(struct ioloop_context *ctx)
1168 {
1169 if (ctx->ioloop->cur_ctx != NULL) {
1170 if (ctx->ioloop->cur_ctx == ctx)
1171 return;
1172 io_loop_context_deactivate(ctx->ioloop->cur_ctx);
1173 /* deactivation may remove the cur_ctx */
1174 if (ctx->ioloop->cur_ctx != NULL)
1175 io_loop_context_unref(&ctx->ioloop->cur_ctx);
1176 }
1177 io_loop_context_activate(ctx);
1178 }
1179
io_loop_get_current_context(struct ioloop * ioloop)1180 struct ioloop_context *io_loop_get_current_context(struct ioloop *ioloop)
1181 {
1182 return ioloop->cur_ctx;
1183 }
1184
io_loop_move_io_to(struct ioloop * ioloop,struct io ** _io)1185 struct io *io_loop_move_io_to(struct ioloop *ioloop, struct io **_io)
1186 {
1187 struct io *old_io = *_io;
1188 struct io_file *old_io_file, *new_io_file;
1189
1190 if (old_io == NULL)
1191 return NULL;
1192
1193 i_assert((old_io->condition & IO_NOTIFY) == 0);
1194
1195 if (old_io->ioloop == ioloop)
1196 return old_io;
1197
1198 old_io_file = (struct io_file *)old_io;
1199 new_io_file = io_add_file(ioloop, old_io_file->fd,
1200 old_io->condition, old_io->source_filename,
1201 old_io->source_linenum,
1202 old_io->callback, old_io->context);
1203 if (old_io_file->istream != NULL) {
1204 /* reference before io_remove() */
1205 new_io_file->istream = old_io_file->istream;
1206 i_stream_ref(new_io_file->istream);
1207 }
1208 if (old_io->pending)
1209 io_set_pending(&new_io_file->io);
1210 io_remove(_io);
1211 if (new_io_file->istream != NULL) {
1212 /* update istream io after it was removed with io_remove() */
1213 i_stream_set_io(new_io_file->istream, &new_io_file->io);
1214 }
1215 return &new_io_file->io;
1216 }
1217
io_loop_move_io(struct io ** _io)1218 struct io *io_loop_move_io(struct io **_io)
1219 {
1220 return io_loop_move_io_to(current_ioloop, _io);
1221 }
1222
io_loop_move_timeout_to(struct ioloop * ioloop,struct timeout ** _timeout)1223 struct timeout *io_loop_move_timeout_to(struct ioloop *ioloop,
1224 struct timeout **_timeout)
1225 {
1226 struct timeout *new_to, *old_to = *_timeout;
1227
1228 if (old_to == NULL || old_to->ioloop == ioloop)
1229 return old_to;
1230
1231 new_to = timeout_copy(old_to, ioloop);
1232 timeout_remove(_timeout);
1233 return new_to;
1234 }
1235
io_loop_move_timeout(struct timeout ** _timeout)1236 struct timeout *io_loop_move_timeout(struct timeout **_timeout)
1237 {
1238 return io_loop_move_timeout_to(current_ioloop, _timeout);
1239 }
1240
io_loop_have_ios(struct ioloop * ioloop)1241 bool io_loop_have_ios(struct ioloop *ioloop)
1242 {
1243 return ioloop->io_files != NULL;
1244 }
1245
io_loop_have_immediate_timeouts(struct ioloop * ioloop)1246 bool io_loop_have_immediate_timeouts(struct ioloop *ioloop)
1247 {
1248 struct timeval tv;
1249
1250 return io_loop_get_wait_time(ioloop, &tv) == 0;
1251 }
1252
io_loop_is_empty(struct ioloop * ioloop)1253 bool io_loop_is_empty(struct ioloop *ioloop)
1254 {
1255 return ioloop->io_files == NULL &&
1256 priorityq_count(ioloop->timeouts) == 0 &&
1257 array_count(&ioloop->timeouts_new) == 0;
1258 }
1259
io_loop_get_wait_usecs(struct ioloop * ioloop)1260 uint64_t io_loop_get_wait_usecs(struct ioloop *ioloop)
1261 {
1262 return ioloop->ioloop_wait_usecs;
1263 }
1264
io_loop_find_fd_conditions(struct ioloop * ioloop,int fd)1265 enum io_condition io_loop_find_fd_conditions(struct ioloop *ioloop, int fd)
1266 {
1267 enum io_condition conditions = 0;
1268 struct io_file *io;
1269
1270 i_assert(fd >= 0);
1271
1272 for (io = ioloop->io_files; io != NULL; io = io->next) {
1273 if (io->fd == fd)
1274 conditions |= io->io.condition;
1275 }
1276 return conditions;
1277 }
1278
1279 #undef io_wait_timer_add_to
1280 struct io_wait_timer *
io_wait_timer_add_to(struct ioloop * ioloop,const char * source_filename,unsigned int source_linenum)1281 io_wait_timer_add_to(struct ioloop *ioloop, const char *source_filename,
1282 unsigned int source_linenum)
1283 {
1284 struct io_wait_timer *timer;
1285
1286 timer = i_new(struct io_wait_timer, 1);
1287 timer->ioloop = ioloop;
1288 timer->source_filename = source_filename;
1289 timer->source_linenum = source_linenum;
1290 DLLIST_PREPEND(&ioloop->wait_timers, timer);
1291 return timer;
1292 }
1293
1294 #undef io_wait_timer_add
1295 struct io_wait_timer *
io_wait_timer_add(const char * source_filename,unsigned int source_linenum)1296 io_wait_timer_add(const char *source_filename, unsigned int source_linenum)
1297 {
1298 return io_wait_timer_add_to(current_ioloop, source_filename,
1299 source_linenum);
1300 }
1301
io_wait_timer_move_to(struct io_wait_timer ** _timer,struct ioloop * ioloop)1302 struct io_wait_timer *io_wait_timer_move_to(struct io_wait_timer **_timer,
1303 struct ioloop *ioloop)
1304 {
1305 struct io_wait_timer *timer = *_timer;
1306
1307 *_timer = NULL;
1308 DLLIST_REMOVE(&timer->ioloop->wait_timers, timer);
1309 DLLIST_PREPEND(&ioloop->wait_timers, timer);
1310 timer->ioloop = ioloop;
1311 return timer;
1312 }
1313
io_wait_timer_move(struct io_wait_timer ** _timer)1314 struct io_wait_timer *io_wait_timer_move(struct io_wait_timer **_timer)
1315 {
1316 return io_wait_timer_move_to(_timer, current_ioloop);
1317 }
1318
io_wait_timer_remove(struct io_wait_timer ** _timer)1319 void io_wait_timer_remove(struct io_wait_timer **_timer)
1320 {
1321 struct io_wait_timer *timer = *_timer;
1322
1323 *_timer = NULL;
1324 DLLIST_REMOVE(&timer->ioloop->wait_timers, timer);
1325 i_free(timer);
1326 }
1327
io_wait_timer_get_usecs(struct io_wait_timer * timer)1328 uint64_t io_wait_timer_get_usecs(struct io_wait_timer *timer)
1329 {
1330 return timer->usecs;
1331 }
1332