Coverage Report

Created: 2026-10-17 06:21

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