]> git.ipfire.org Git - thirdparty/chrony.git/blame - sched.c
conf: rework allow/deny parser
[thirdparty/chrony.git] / sched.c
CommitLineData
88840341 1/*
88840341
RC
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
6672f045 5 * Copyright (C) Richard P. Curnow 1997-2003
a4e3f836 6 * Copyright (C) Miroslav Lichvar 2011, 2013-2016
88840341
RC
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
8e23110a 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
88840341
RC
20 *
21 **********************************************************************
22
23 =======================================================================
24
25 This file contains the scheduling loop and the timeout queue.
26
27 */
28
da2c8d90
ML
29#include "config.h"
30
88840341
RC
31#include "sysincl.h"
32
ba875fc0 33#include "array.h"
88840341
RC
34#include "sched.h"
35#include "memory.h"
36#include "util.h"
37#include "local.h"
38#include "logging.h"
39
40/* ================================================== */
41
42/* Flag indicating that we are initialised */
43static int initialised = 0;
44
45/* ================================================== */
46
88840341
RC
47/* One more than the highest file descriptor that is registered */
48static unsigned int one_highest_fd;
49
a2b40f52
ML
50#ifndef FD_SETSIZE
51/* If FD_SETSIZE is not defined, assume that fd_set is implemented
52 as a fixed size array of bits, possibly embedded inside a record */
53#define FD_SETSIZE (sizeof(fd_set) * 8)
54#endif
88840341
RC
55
56typedef struct {
57 SCH_FileHandler handler;
58 SCH_ArbitraryArgument arg;
d8d096aa 59 int events;
88840341
RC
60} FileHandlerEntry;
61
ba875fc0 62static ARR_Instance file_handlers;
88840341 63
91749ebb 64/* Timestamp when last select() returned */
d0dfa1de 65static struct timespec last_select_ts, last_select_ts_raw;
91749ebb 66static double last_select_ts_err;
e9ae3d0a 67
bb0553e4
ML
68#define TS_MONO_PRECISION_NS 10000000U
69
70/* Monotonic low-precision timestamp measuring interval since the start */
71static double last_select_ts_mono;
72static uint32_t last_select_ts_mono_ns;
73
88840341
RC
74/* ================================================== */
75
76/* Variables to handler the timer queue */
77
78typedef struct _TimerQueueEntry
79{
80 struct _TimerQueueEntry *next; /* Forward and back links in the list */
81 struct _TimerQueueEntry *prev;
d0dfa1de 82 struct timespec ts; /* Local system time at which the
88840341
RC
83 timeout is to expire. Clearly this
84 must be in terms of what the
85 operating system thinks of as
86 system time, because it will be an
87 argument to select(). Therefore,
88 any fudges etc that our local time
89 driver module would apply to time
90 that we pass to clients etc doesn't
91 apply to this. */
92 SCH_TimeoutID id; /* ID to allow client to delete
93 timeout */
94 SCH_TimeoutClass class; /* The class that the epoch is in */
95 SCH_TimeoutHandler handler; /* The handler routine to use */
96 SCH_ArbitraryArgument arg; /* The argument to pass to the handler */
97
98} TimerQueueEntry;
99
100/* The timer queue. We only use the next and prev entries of this
101 record, these chain to the real entries. */
102static TimerQueueEntry timer_queue;
103static unsigned long n_timer_queue_entries;
104static SCH_TimeoutID next_tqe_id;
105
106/* Pointer to head of free list */
107static TimerQueueEntry *tqe_free_list = NULL;
108
0078705b 109/* Timestamp when was last timeout dispatched for each class */
d0dfa1de 110static struct timespec last_class_dispatch[SCH_NumberOfClasses];
0078705b 111
88840341
RC
112/* ================================================== */
113
3812ec2a
ML
114/* Flag terminating the main loop, which can be set from a signal handler */
115static volatile int need_to_exit;
88840341
RC
116
117/* ================================================== */
118
119static void
d0dfa1de
ML
120handle_slew(struct timespec *raw,
121 struct timespec *cooked,
88840341 122 double dfreq,
88840341 123 double doffset,
44c9744d 124 LCL_ChangeType change_type,
88840341
RC
125 void *anything);
126
127/* ================================================== */
128
129void
130SCH_Initialise(void)
131{
ba875fc0
ML
132 file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry));
133
88840341
RC
134 n_timer_queue_entries = 0;
135 next_tqe_id = 0;
136
137 timer_queue.next = &timer_queue;
138 timer_queue.prev = &timer_queue;
139
140 need_to_exit = 0;
141
142 LCL_AddParameterChangeHandler(handle_slew, NULL);
143
6fa11a85
ML
144 LCL_ReadRawTime(&last_select_ts_raw);
145 last_select_ts = last_select_ts_raw;
bb0553e4
ML
146 last_select_ts_mono = 0.0;
147 last_select_ts_mono_ns = 0;
6fa11a85 148
88840341 149 initialised = 1;
88840341
RC
150}
151
152
153/* ================================================== */
154
155void
156SCH_Finalise(void) {
ba875fc0
ML
157 ARR_DestroyInstance(file_handlers);
158
3e537416
ML
159 LCL_RemoveParameterChangeHandler(handle_slew, NULL);
160
88840341 161 initialised = 0;
88840341
RC
162}
163
164/* ================================================== */
165
166void
0a105453
ML
167SCH_AddFileHandler
168(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
88840341 169{
ba875fc0 170 FileHandlerEntry *ptr;
88840341 171
6b0198c2 172 assert(initialised);
0a105453
ML
173 assert(events);
174 assert(fd >= 0);
88840341 175
07f7f280 176 if (fd >= FD_SETSIZE)
f282856c 177 LOG_FATAL("Too many file descriptors");
07f7f280 178
d8d096aa
ML
179 /* Resize the array if the descriptor is highest so far */
180 while (ARR_GetSize(file_handlers) <= fd) {
181 ptr = ARR_GetNewElement(file_handlers);
182 ptr->handler = NULL;
183 ptr->arg = NULL;
184 ptr->events = 0;
185 }
186
187 ptr = ARR_GetElement(file_handlers, fd);
188
88840341
RC
189 /* Don't want to allow the same fd to register a handler more than
190 once without deleting a previous association - this suggests
191 a bug somewhere else in the program. */
d8d096aa 192 assert(!ptr->handler);
ba875fc0 193
ba875fc0
ML
194 ptr->handler = handler;
195 ptr->arg = arg;
d8d096aa 196 ptr->events = events;
88840341 197
d8d096aa 198 if (one_highest_fd < fd + 1)
88840341 199 one_highest_fd = fd + 1;
88840341
RC
200}
201
202
203/* ================================================== */
204
205void
0a105453 206SCH_RemoveFileHandler(int fd)
88840341 207{
d8d096aa 208 FileHandlerEntry *ptr;
88840341 209
6b0198c2 210 assert(initialised);
88840341 211
d8d096aa 212 ptr = ARR_GetElement(file_handlers, fd);
88840341 213
d8d096aa
ML
214 /* Check that a handler was registered for the fd in question */
215 assert(ptr->handler);
88840341 216
d8d096aa
ML
217 ptr->handler = NULL;
218 ptr->arg = NULL;
219 ptr->events = 0;
88840341
RC
220
221 /* Find new highest file descriptor */
d8d096aa
ML
222 while (one_highest_fd > 0) {
223 ptr = ARR_GetElement(file_handlers, one_highest_fd - 1);
224 if (ptr->handler)
225 break;
226 one_highest_fd--;
88840341 227 }
88840341
RC
228}
229
230/* ================================================== */
231
0a105453 232void
2c4c2351 233SCH_SetFileHandlerEvent(int fd, int event, int enable)
0a105453
ML
234{
235 FileHandlerEntry *ptr;
236
0a105453 237 ptr = ARR_GetElement(file_handlers, fd);
2c4c2351
ML
238
239 if (enable)
240 ptr->events |= event;
241 else
242 ptr->events &= ~event;
0a105453
ML
243}
244
245/* ================================================== */
246
e9ae3d0a 247void
d0dfa1de 248SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw)
e9ae3d0a 249{
58f76892
ML
250 if (cooked) {
251 *cooked = last_select_ts;
252 if (err)
253 *err = last_select_ts_err;
254 }
255 if (raw)
256 *raw = last_select_ts_raw;
e9ae3d0a
ML
257}
258
259/* ================================================== */
260
bb0553e4
ML
261double
262SCH_GetLastEventMonoTime(void)
263{
264 return last_select_ts_mono;
265}
266
267/* ================================================== */
268
88840341
RC
269#define TQE_ALLOC_QUANTUM 32
270
271static TimerQueueEntry *
272allocate_tqe(void)
273{
274 TimerQueueEntry *new_block;
275 TimerQueueEntry *result;
276 int i;
277 if (tqe_free_list == NULL) {
278 new_block = MallocArray(TimerQueueEntry, TQE_ALLOC_QUANTUM);
279 for (i=1; i<TQE_ALLOC_QUANTUM; i++) {
280 new_block[i].next = &(new_block[i-1]);
281 }
282 new_block[0].next = NULL;
283 tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
284 }
285
286 result = tqe_free_list;
287 tqe_free_list = tqe_free_list->next;
288 return result;
289}
290
291/* ================================================== */
292
293static void
294release_tqe(TimerQueueEntry *node)
295{
296 node->next = tqe_free_list;
297 tqe_free_list = node;
88840341
RC
298}
299
300/* ================================================== */
301
0076458e
ML
302static SCH_TimeoutID
303get_new_tqe_id(void)
304{
38910424
ML
305 TimerQueueEntry *ptr;
306
307try_again:
0076458e
ML
308 next_tqe_id++;
309 if (!next_tqe_id)
38910424
ML
310 goto try_again;
311
312 /* Make sure the ID isn't already used */
313 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next)
314 if (ptr->id == next_tqe_id)
315 goto try_again;
0076458e
ML
316
317 return next_tqe_id;
318}
319
320/* ================================================== */
321
88840341 322SCH_TimeoutID
d0dfa1de 323SCH_AddTimeout(struct timespec *ts, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
88840341
RC
324{
325 TimerQueueEntry *new_tqe;
326 TimerQueueEntry *ptr;
327
6b0198c2 328 assert(initialised);
88840341
RC
329
330 new_tqe = allocate_tqe();
331
0076458e 332 new_tqe->id = get_new_tqe_id();
88840341
RC
333 new_tqe->handler = handler;
334 new_tqe->arg = arg;
d0dfa1de 335 new_tqe->ts = *ts;
88840341
RC
336 new_tqe->class = SCH_ReservedTimeoutValue;
337
338 /* Now work out where to insert the new entry in the list */
339 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
d0dfa1de 340 if (UTI_CompareTimespecs(&new_tqe->ts, &ptr->ts) == -1) {
88840341
RC
341 /* If the new entry comes before the current pointer location in
342 the list, we want to insert the new entry just before ptr. */
343 break;
344 }
345 }
346
347 /* At this stage, we want to insert the new entry immediately before
348 the entry identified by 'ptr' */
349
350 new_tqe->next = ptr;
351 new_tqe->prev = ptr->prev;
352 ptr->prev->next = new_tqe;
353 ptr->prev = new_tqe;
354
355 n_timer_queue_entries++;
356
357 return new_tqe->id;
358}
359
360/* ================================================== */
361/* This queues a timeout to elapse at a given delta time relative to
362 the current (raw) time */
363
364SCH_TimeoutID
365SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
366{
d0dfa1de 367 struct timespec now, then;
88840341 368
6b0198c2 369 assert(initialised);
bab7ba22 370 assert(delay >= 0.0);
88840341
RC
371
372 LCL_ReadRawTime(&now);
d0dfa1de
ML
373 UTI_AddDoubleToTimespec(&now, delay, &then);
374 if (UTI_CompareTimespecs(&now, &then) > 0) {
f282856c 375 LOG_FATAL("Timeout overflow");
ea7fae52
ML
376 }
377
88840341
RC
378 return SCH_AddTimeout(&then, handler, arg);
379
380}
381
382/* ================================================== */
383
384SCH_TimeoutID
833022b7 385SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
88840341
RC
386 SCH_TimeoutClass class,
387 SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
388{
389 TimerQueueEntry *new_tqe;
390 TimerQueueEntry *ptr;
d0dfa1de 391 struct timespec now;
833022b7 392 double diff, r;
88840341
RC
393 double new_min_delay;
394
6b0198c2 395 assert(initialised);
bab7ba22 396 assert(min_delay >= 0.0);
0078705b 397 assert(class < SCH_NumberOfClasses);
833022b7
ML
398
399 if (randomness > 0.0) {
4b0ef092 400 uint32_t rnd;
dfc96e47
ML
401
402 UTI_GetRandomBytes(&rnd, sizeof (rnd));
4b0ef092 403 r = rnd * (randomness / (uint32_t)-1) + 1.0;
833022b7
ML
404 min_delay *= r;
405 separation *= r;
406 }
88840341
RC
407
408 LCL_ReadRawTime(&now);
409 new_min_delay = min_delay;
410
0078705b 411 /* Check the separation from the last dispatched timeout */
cfe706f0 412 diff = UTI_DiffTimespecsToDouble(&now, &last_class_dispatch[class]);
0078705b
ML
413 if (diff < separation && diff >= 0.0 && diff + new_min_delay < separation) {
414 new_min_delay = separation - diff;
415 }
416
88840341
RC
417 /* Scan through list for entries in the same class and increase min_delay
418 if necessary to keep at least the separation away */
419 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
420 if (ptr->class == class) {
cfe706f0 421 diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
88840341
RC
422 if (new_min_delay > diff) {
423 if (new_min_delay - diff < separation) {
424 new_min_delay = diff + separation;
425 }
4373918a 426 } else {
88840341
RC
427 if (diff - new_min_delay < separation) {
428 new_min_delay = diff + separation;
429 }
430 }
431 }
432 }
433
434 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
cfe706f0 435 diff = UTI_DiffTimespecsToDouble(&ptr->ts, &now);
88840341
RC
436 if (diff > new_min_delay) {
437 break;
438 }
439 }
440
441 /* We have located the insertion point */
442 new_tqe = allocate_tqe();
443
0076458e 444 new_tqe->id = get_new_tqe_id();
88840341
RC
445 new_tqe->handler = handler;
446 new_tqe->arg = arg;
d0dfa1de 447 UTI_AddDoubleToTimespec(&now, new_min_delay, &new_tqe->ts);
88840341
RC
448 new_tqe->class = class;
449
450 new_tqe->next = ptr;
451 new_tqe->prev = ptr->prev;
452 ptr->prev->next = new_tqe;
453 ptr->prev = new_tqe;
454 n_timer_queue_entries++;
455
456 return new_tqe->id;
457}
458
459/* ================================================== */
460
461void
462SCH_RemoveTimeout(SCH_TimeoutID id)
463{
464 TimerQueueEntry *ptr;
88840341 465
6b0198c2 466 assert(initialised);
88840341 467
0076458e
ML
468 if (!id)
469 return;
470
88840341
RC
471 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
472
473 if (ptr->id == id) {
474 /* Found the required entry */
475
476 /* Unlink from the queue */
477 ptr->next->prev = ptr->prev;
478 ptr->prev->next = ptr->next;
479
480 /* Decrement entry count */
481 --n_timer_queue_entries;
482
483 /* Release memory back to the operating system */
484 release_tqe(ptr);
485
8803ab27 486 return;
88840341
RC
487 }
488 }
8803ab27
ML
489
490 /* Catch calls with invalid non-zero ID */
491 assert(0);
88840341
RC
492}
493
494/* ================================================== */
dce2366b
ML
495/* Try to dispatch any timeouts that have already gone by, and
496 keep going until all are done. (The earlier ones may take so
497 long to do that the later ones come around by the time they are
498 completed). */
88840341 499
dce2366b 500static void
d0dfa1de 501dispatch_timeouts(struct timespec *now) {
59e8b790 502 unsigned long n_done, n_entries_on_start;
88840341 503 TimerQueueEntry *ptr;
1d6b94b4
ML
504 SCH_TimeoutHandler handler;
505 SCH_ArbitraryArgument arg;
59e8b790
ML
506
507 n_entries_on_start = n_timer_queue_entries;
508 n_done = 0;
dce2366b 509
e7897eb9 510 do {
dce2366b
ML
511 LCL_ReadRawTime(now);
512
513 if (!(n_timer_queue_entries > 0 &&
d0dfa1de 514 UTI_CompareTimespecs(now, &timer_queue.next->ts) >= 0)) {
dce2366b
ML
515 break;
516 }
88840341 517
88840341
RC
518 ptr = timer_queue.next;
519
0078705b
ML
520 last_class_dispatch[ptr->class] = *now;
521
1d6b94b4
ML
522 handler = ptr->handler;
523 arg = ptr->arg;
524
525 SCH_RemoveTimeout(ptr->id);
526
88840341 527 /* Dispatch the handler */
1d6b94b4 528 (handler)(arg);
88840341
RC
529
530 /* Increment count of timeouts handled */
531 ++n_done;
88840341 532
59e8b790
ML
533 /* If the number of dispatched timeouts is significantly larger than the
534 length of the queue on start and now, assume there is a bug causing
535 an infinite loop by constantly adding a timeout with a zero or negative
536 delay. Check the actual rate of timeouts to avoid false positives in
537 case the execution slowed down so much (e.g. due to memory thrashing)
538 that it repeatedly takes more time to handle the timeout than is its
539 delay. This is a safety mechanism intended to stop a full-speed flood
540 of NTP requests due to a bug in the NTP polling. */
541
542 if (n_done > 20 &&
543 n_done > 4 * MAX(n_timer_queue_entries, n_entries_on_start) &&
544 fabs(UTI_DiffTimespecsToDouble(now, &last_select_ts_raw)) / n_done < 0.01)
f282856c 545 LOG_FATAL("Possible infinite loop in scheduling");
e7897eb9
ML
546
547 } while (!need_to_exit);
88840341
RC
548}
549
550/* ================================================== */
551
57fc2ff1 552/* nfd is the number of bits set in all fd_sets */
88840341
RC
553
554static void
c169ad3f 555dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds)
88840341 556{
ba875fc0 557 FileHandlerEntry *ptr;
57fc2ff1 558 int fd;
88840341 559
57fc2ff1 560 for (fd = 0; nfd && fd < one_highest_fd; fd++) {
c169ad3f
ML
561 if (except_fds && FD_ISSET(fd, except_fds)) {
562 /* This descriptor has an exception, dispatch its handler */
563 ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
a8167b79
ML
564 if (ptr->handler)
565 (ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
c169ad3f
ML
566 nfd--;
567
568 /* Don't try to read from it now */
569 if (read_fds && FD_ISSET(fd, read_fds)) {
570 FD_CLR(fd, read_fds);
571 nfd--;
572 }
573 }
574
57fc2ff1 575 if (read_fds && FD_ISSET(fd, read_fds)) {
88840341 576 /* This descriptor can be read from, dispatch its handler */
57fc2ff1 577 ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
a8167b79
ML
578 if (ptr->handler)
579 (ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
57fc2ff1 580 nfd--;
88840341
RC
581 }
582
57fc2ff1
ML
583 if (write_fds && FD_ISSET(fd, write_fds)) {
584 /* This descriptor can be written to, dispatch its handler */
585 ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
a8167b79
ML
586 if (ptr->handler)
587 (ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
57fc2ff1
ML
588 nfd--;
589 }
88840341 590 }
88840341
RC
591}
592
593/* ================================================== */
594
595static void
d0dfa1de
ML
596handle_slew(struct timespec *raw,
597 struct timespec *cooked,
88840341 598 double dfreq,
88840341 599 double doffset,
44c9744d 600 LCL_ChangeType change_type,
88840341
RC
601 void *anything)
602{
603 TimerQueueEntry *ptr;
41805d57 604 double delta;
59c68d24 605 int i;
88840341 606
44c9744d 607 if (change_type != LCL_ChangeAdjust) {
0bdac2c7
ML
608 /* Make sure this handler is invoked first in order to not shift new timers
609 added from other handlers */
610 assert(LCL_IsFirstParameterChangeHandler(handle_slew));
611
41805d57 612 /* If a step change occurs, just shift all raw time stamps by the offset */
88840341
RC
613
614 for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
d0dfa1de 615 UTI_AddDoubleToTimespec(&ptr->ts, -doffset, &ptr->ts);
88840341
RC
616 }
617
59c68d24 618 for (i = 0; i < SCH_NumberOfClasses; i++) {
d0dfa1de 619 UTI_AddDoubleToTimespec(&last_class_dispatch[i], -doffset, &last_class_dispatch[i]);
59c68d24 620 }
0bf34725 621
d0dfa1de 622 UTI_AddDoubleToTimespec(&last_select_ts_raw, -doffset, &last_select_ts_raw);
88840341 623 }
41805d57 624
d0dfa1de 625 UTI_AdjustTimespec(&last_select_ts, cooked, &last_select_ts, &delta, dfreq, doffset);
88840341
RC
626}
627
628/* ================================================== */
629
d8d096aa 630static void
c169ad3f 631fill_fd_sets(fd_set **read_fds, fd_set **write_fds, fd_set **except_fds)
d8d096aa
ML
632{
633 FileHandlerEntry *handlers;
c169ad3f 634 fd_set *rd, *wr, *ex;
d8d096aa
ML
635 int i, n, events;
636
637 n = ARR_GetSize(file_handlers);
638 handlers = ARR_GetElements(file_handlers);
c169ad3f 639 rd = wr = ex = NULL;
d8d096aa
ML
640
641 for (i = 0; i < n; i++) {
642 events = handlers[i].events;
643
644 if (!events)
645 continue;
646
647 if (events & SCH_FILE_INPUT) {
648 if (!rd) {
649 rd = *read_fds;
650 FD_ZERO(rd);
651 }
652 FD_SET(i, rd);
653 }
57fc2ff1
ML
654
655 if (events & SCH_FILE_OUTPUT) {
656 if (!wr) {
657 wr = *write_fds;
658 FD_ZERO(wr);
659 }
660 FD_SET(i, wr);
661 }
c169ad3f
ML
662
663 if (events & SCH_FILE_EXCEPTION) {
664 if (!ex) {
665 ex = *except_fds;
666 FD_ZERO(ex);
667 }
668 FD_SET(i, ex);
669 }
d8d096aa
ML
670 }
671
672 if (!rd)
673 *read_fds = NULL;
57fc2ff1
ML
674 if (!wr)
675 *write_fds = NULL;
c169ad3f
ML
676 if (!ex)
677 *except_fds = NULL;
d8d096aa
ML
678}
679
680/* ================================================== */
681
a3e60c93 682#define JUMP_DETECT_THRESHOLD 10
91749ebb 683
a3e60c93 684static int
d0dfa1de 685check_current_time(struct timespec *prev_raw, struct timespec *raw, int timeout,
e63bd490
ML
686 struct timeval *orig_select_tv,
687 struct timeval *rem_select_tv)
91749ebb 688{
d0dfa1de 689 struct timespec elapsed_min, elapsed_max, orig_select_ts, rem_select_ts;
e63bd490 690 double step, elapsed;
a3e60c93 691
d0dfa1de
ML
692 UTI_TimevalToTimespec(orig_select_tv, &orig_select_ts);
693
e63bd490
ML
694 /* Get an estimate of the time spent waiting in the select() call. On some
695 systems (e.g. Linux) the timeout timeval is modified to return the
696 remaining time, use that information. */
697 if (timeout) {
d0dfa1de 698 elapsed_max = elapsed_min = orig_select_ts;
e63bd490
ML
699 } else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
700 rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
701 (rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
702 rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
d0dfa1de
ML
703 UTI_TimevalToTimespec(rem_select_tv, &rem_select_ts);
704 UTI_DiffTimespecs(&elapsed_min, &orig_select_ts, &rem_select_ts);
e63bd490
ML
705 elapsed_max = elapsed_min;
706 } else {
707 if (rem_select_tv)
d0dfa1de 708 elapsed_max = orig_select_ts;
e63bd490 709 else
d0dfa1de
ML
710 UTI_DiffTimespecs(&elapsed_max, raw, prev_raw);
711 UTI_ZeroTimespec(&elapsed_min);
e63bd490
ML
712 }
713
714 if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
715 raw->tv_sec + JUMP_DETECT_THRESHOLD) {
f282856c 716 LOG(LOGS_WARN, "Backward time jump detected!");
e63bd490
ML
717 } else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
718 raw->tv_sec) {
f282856c 719 LOG(LOGS_WARN, "Forward time jump detected!");
a3e60c93
ML
720 } else {
721 return 1;
722 }
91749ebb 723
cfe706f0
ML
724 step = UTI_DiffTimespecsToDouble(&last_select_ts_raw, raw);
725 elapsed = UTI_TimespecToDouble(&elapsed_min);
e63bd490 726 step += elapsed;
91749ebb 727
a3e60c93 728 /* Cooked time may no longer be valid after dispatching the handlers */
e63bd490 729 LCL_NotifyExternalTimeStep(raw, raw, step, fabs(step));
91749ebb 730
a3e60c93 731 return 0;
91749ebb
ML
732}
733
734/* ================================================== */
735
bb0553e4
ML
736static void
737update_monotonic_time(struct timespec *now, struct timespec *before)
738{
739 struct timespec diff;
740
741 /* Avoid frequent floating-point operations and handle small
742 increments to a large value */
743
744 UTI_DiffTimespecs(&diff, now, before);
745 if (diff.tv_sec == 0) {
746 last_select_ts_mono_ns += diff.tv_nsec;
747 } else {
748 last_select_ts_mono += fabs(UTI_TimespecToDouble(&diff) +
749 last_select_ts_mono_ns / 1.0e9);
750 last_select_ts_mono_ns = 0;
751 }
752
753 if (last_select_ts_mono_ns > TS_MONO_PRECISION_NS) {
754 last_select_ts_mono += last_select_ts_mono_ns / 1.0e9;
755 last_select_ts_mono_ns = 0;
756 }
757}
758
759/* ================================================== */
760
88840341
RC
761void
762SCH_MainLoop(void)
763{
c169ad3f
ML
764 fd_set read_fds, write_fds, except_fds;
765 fd_set *p_read_fds, *p_write_fds, *p_except_fds;
f7802f01 766 int status, errsv;
e63bd490 767 struct timeval tv, saved_tv, *ptv;
d0dfa1de 768 struct timespec ts, now, saved_now, cooked;
91749ebb 769 double err;
88840341 770
6b0198c2 771 assert(initialised);
88840341
RC
772
773 while (!need_to_exit) {
dce2366b
ML
774 /* Dispatch timeouts and fill now with current raw time */
775 dispatch_timeouts(&now);
e63bd490 776 saved_now = now;
88840341 777
5cb7e6c9
ML
778 /* The timeout handlers may request quit */
779 if (need_to_exit)
780 break;
781
88840341
RC
782 /* Check whether there is a timeout and set it up */
783 if (n_timer_queue_entries > 0) {
d0dfa1de
ML
784 UTI_DiffTimespecs(&ts, &timer_queue.next->ts, &now);
785 assert(ts.tv_sec > 0 || ts.tv_nsec > 0);
88840341 786
d0dfa1de 787 UTI_TimespecToTimeval(&ts, &tv);
88840341 788 ptv = &tv;
e63bd490 789 saved_tv = tv;
88840341
RC
790 } else {
791 ptv = NULL;
1afb285a 792 saved_tv.tv_sec = saved_tv.tv_usec = 0;
88840341
RC
793 }
794
d8d096aa 795 p_read_fds = &read_fds;
57fc2ff1 796 p_write_fds = &write_fds;
c169ad3f
ML
797 p_except_fds = &except_fds;
798 fill_fd_sets(&p_read_fds, &p_write_fds, &p_except_fds);
d8d096aa 799
88840341
RC
800 /* if there are no file descriptors being waited on and no
801 timeout set, this is clearly ridiculous, so stop the run */
57fc2ff1 802 if (!ptv && !p_read_fds && !p_write_fds)
f282856c 803 LOG_FATAL("Nothing to do");
5cb7e6c9 804
c169ad3f 805 status = select(one_highest_fd, p_read_fds, p_write_fds, p_except_fds, ptv);
f7802f01 806 errsv = errno;
88840341 807
91749ebb
ML
808 LCL_ReadRawTime(&now);
809 LCL_CookTime(&now, &cooked, &err);
810
f15f6a86
ML
811 update_monotonic_time(&now, &last_select_ts_raw);
812
a3e60c93 813 /* Check if the time didn't jump unexpectedly */
e63bd490 814 if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) {
a3e60c93
ML
815 /* Cook the time again after handling the step */
816 LCL_CookTime(&now, &cooked, &err);
91749ebb
ML
817 }
818
819 last_select_ts_raw = now;
820 last_select_ts = cooked;
821 last_select_ts_err = err;
822
88840341 823 if (status < 0) {
f7802f01 824 if (!need_to_exit && errsv != EINTR) {
f282856c 825 LOG_FATAL("select() failed : %s", strerror(errsv));
f7802f01 826 }
88840341 827 } else if (status > 0) {
57fc2ff1 828 /* A file descriptor is ready for input or output */
c169ad3f 829 dispatch_filehandlers(status, p_read_fds, p_write_fds, p_except_fds);
88840341 830 } else {
88840341
RC
831 /* No descriptors readable, timeout must have elapsed.
832 Therefore, tv must be non-null */
6b0198c2 833 assert(ptv);
88840341
RC
834
835 /* There's nothing to do here, since the timeouts
836 will be dispatched at the top of the next loop
837 cycle */
838
839 }
840 }
88840341
RC
841}
842
843/* ================================================== */
844
845void
846SCH_QuitProgram(void)
847{
88840341
RC
848 need_to_exit = 1;
849}
850
851/* ================================================== */
852