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