X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-bus%2Fsd-event.c;h=9fceb7b13edd794b99655e0d644244047678d71b;hb=cde93897cdefdd7c7f66c400a61e42ceee5f6a46;hp=ace97dc7393767f3fbdfa22a11cdba552b2557df;hpb=d09d5ecda2172ba539f1b08da9f3e9787be37229;p=elogind.git diff --git a/src/libsystemd-bus/sd-event.c b/src/libsystemd-bus/sd-event.c index ace97dc73..9fceb7b13 100644 --- a/src/libsystemd-bus/sd-event.c +++ b/src/libsystemd-bus/sd-event.c @@ -23,12 +23,14 @@ #include #include +#include "sd-id128.h" +#include "sd-daemon.h" #include "macro.h" #include "prioq.h" #include "hashmap.h" #include "util.h" #include "time-util.h" -#include "sd-id128.h" +#include "missing.h" #include "sd-event.h" @@ -42,7 +44,8 @@ typedef enum EventSourceType { SOURCE_SIGNAL, SOURCE_CHILD, SOURCE_DEFER, - SOURCE_QUIT + SOURCE_QUIT, + SOURCE_WATCHDOG } EventSourceType; struct sd_event_source { @@ -50,7 +53,7 @@ struct sd_event_source { sd_event *event; void *userdata; - sd_prepare_handler_t prepare; + sd_event_handler_t prepare; EventSourceType type:4; int enabled:3; @@ -64,34 +67,34 @@ struct sd_event_source { union { struct { - sd_io_handler_t callback; + sd_event_io_handler_t callback; int fd; uint32_t events; uint32_t revents; bool registered:1; } io; struct { - sd_time_handler_t callback; + sd_event_time_handler_t callback; usec_t next, accuracy; unsigned earliest_index; unsigned latest_index; } time; struct { - sd_signal_handler_t callback; + sd_event_signal_handler_t callback; struct signalfd_siginfo siginfo; int sig; } signal; struct { - sd_child_handler_t callback; + sd_event_child_handler_t callback; siginfo_t siginfo; pid_t pid; int options; } child; struct { - sd_defer_handler_t callback; + sd_event_handler_t callback; } defer; struct { - sd_quit_handler_t callback; + sd_event_handler_t callback; unsigned prioq_index; } quit; }; @@ -104,6 +107,7 @@ struct sd_event { int signal_fd; int realtime_fd; int monotonic_fd; + int watchdog_fd; Prioq *pending; Prioq *prepare; @@ -138,6 +142,12 @@ struct sd_event { bool quit_requested:1; bool need_process_child:1; + bool watchdog:1; + + pid_t tid; + sd_event **default_event_ptr; + + usec_t watchdog_last, watchdog_period; }; static int pending_prioq_compare(const void *a, const void *b) { @@ -230,7 +240,7 @@ static int earliest_time_prioq_compare(const void *a, const void *b) { if (x->time.next < y->time.next) return -1; if (x->time.next > y->time.next) - return -1; + return 1; /* Stability for the rest */ if (x < y) @@ -263,7 +273,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) { if (x->time.next + x->time.accuracy < y->time.next + y->time.accuracy) return -1; if (x->time.next + x->time.accuracy > y->time.next + y->time.accuracy) - return -1; + return 1; /* Stability for the rest */ if (x < y) @@ -304,6 +314,9 @@ static int quit_prioq_compare(const void *a, const void *b) { static void event_free(sd_event *e) { assert(e); + if (e->default_event_ptr) + *(e->default_event_ptr) = NULL; + if (e->epoll_fd >= 0) close_nointr_nofail(e->epoll_fd); @@ -316,6 +329,9 @@ static void event_free(sd_event *e) { if (e->monotonic_fd >= 0) close_nointr_nofail(e->monotonic_fd); + if (e->watchdog_fd >= 0) + close_nointr_nofail(e->watchdog_fd); + prioq_free(e->pending); prioq_free(e->prepare); prioq_free(e->monotonic_earliest); @@ -330,7 +346,7 @@ static void event_free(sd_event *e) { free(e); } -int sd_event_new(sd_event** ret) { +_public_ int sd_event_new(sd_event** ret) { sd_event *e; int r; @@ -341,7 +357,7 @@ int sd_event_new(sd_event** ret) { return -ENOMEM; e->n_ref = 1; - e->signal_fd = e->realtime_fd = e->monotonic_fd = e->epoll_fd = -1; + e->signal_fd = e->realtime_fd = e->monotonic_fd = e->watchdog_fd = e->epoll_fd = -1; e->realtime_next = e->monotonic_next = (usec_t) -1; e->original_pid = getpid(); @@ -367,7 +383,7 @@ fail: return r; } -sd_event* sd_event_ref(sd_event *e) { +_public_ sd_event* sd_event_ref(sd_event *e) { assert_return(e, NULL); assert(e->n_ref >= 1); @@ -376,8 +392,10 @@ sd_event* sd_event_ref(sd_event *e) { return e; } -sd_event* sd_event_unref(sd_event *e) { - assert_return(e, NULL); +_public_ sd_event* sd_event_unref(sd_event *e) { + + if (!e) + return NULL; assert(e->n_ref >= 1); e->n_ref--; @@ -536,6 +554,14 @@ static int source_set_pending(sd_event_source *s, bool b) { } else assert_se(prioq_remove(s->event->pending, s, &s->pending_index)); + if (s->type == SOURCE_REALTIME) { + prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index); + prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index); + } else if (s->type == SOURCE_MONOTONIC) { + prioq_reshuffle(s->event->monotonic_earliest, s, &s->time.earliest_index); + prioq_reshuffle(s->event->monotonic_latest, s, &s->time.latest_index); + } + return 0; } @@ -556,11 +582,11 @@ static sd_event_source *source_new(sd_event *e, EventSourceType type) { return s; } -int sd_event_add_io( +_public_ int sd_event_add_io( sd_event *e, int fd, uint32_t events, - sd_io_handler_t callback, + sd_event_io_handler_t callback, void *userdata, sd_event_source **ret) { @@ -569,7 +595,7 @@ int sd_event_add_io( assert_return(e, -EINVAL); assert_return(fd >= 0, -EINVAL); - assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP)), -EINVAL); + assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); assert_return(callback, -EINVAL); assert_return(ret, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); @@ -625,15 +651,16 @@ static int event_setup_timer_fd( } /* When we sleep for longer, we try to realign the wakeup to - the same time wihtin each second, so that events all across - the system can be coalesced into a single CPU - wakeup. However, let's take some system-specific randomness - for this value, so that in a network of systems with synced - clocks timer events are distributed a bit. Here, we - calculate a perturbation usec offset from the boot ID. */ + the same time wihtin each minute/second/250ms, so that + events all across the system can be coalesced into a single + CPU wakeup. However, let's take some system-specific + randomness for this value, so that in a network of systems + with synced clocks timer events are distributed a + bit. Here, we calculate a perturbation usec offset from the + boot ID. */ if (sd_id128_get_boot(&bootid) >= 0) - e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_SEC; + e->perturb = (bootid.qwords[0] ^ bootid.qwords[1]) % USEC_PER_MINUTE; *timer_fd = fd; return 0; @@ -648,7 +675,7 @@ static int event_add_time_internal( Prioq **latest, uint64_t usec, uint64_t accuracy, - sd_time_handler_t callback, + sd_event_time_handler_t callback, void *userdata, sd_event_source **ret) { @@ -712,11 +739,23 @@ fail: return r; } -int sd_event_add_monotonic(sd_event *e, uint64_t usec, uint64_t accuracy, sd_time_handler_t callback, void *userdata, sd_event_source **ret) { +_public_ int sd_event_add_monotonic(sd_event *e, + uint64_t usec, + uint64_t accuracy, + sd_event_time_handler_t callback, + void *userdata, + sd_event_source **ret) { + return event_add_time_internal(e, SOURCE_MONOTONIC, &e->monotonic_fd, CLOCK_MONOTONIC, &e->monotonic_earliest, &e->monotonic_latest, usec, accuracy, callback, userdata, ret); } -int sd_event_add_realtime(sd_event *e, uint64_t usec, uint64_t accuracy, sd_time_handler_t callback, void *userdata, sd_event_source **ret) { +_public_ int sd_event_add_realtime(sd_event *e, + uint64_t usec, + uint64_t accuracy, + sd_event_time_handler_t callback, + void *userdata, + sd_event_source **ret) { + return event_add_time_internal(e, SOURCE_REALTIME, &e->realtime_fd, CLOCK_REALTIME, &e->realtime_earliest, &e->monotonic_latest, usec, accuracy, callback, userdata, ret); } @@ -752,10 +791,10 @@ static int event_update_signal_fd(sd_event *e) { return 0; } -int sd_event_add_signal( +_public_ int sd_event_add_signal( sd_event *e, int sig, - sd_signal_handler_t callback, + sd_event_signal_handler_t callback, void *userdata, sd_event_source **ret) { @@ -801,11 +840,11 @@ int sd_event_add_signal( return 0; } -int sd_event_add_child( +_public_ int sd_event_add_child( sd_event *e, pid_t pid, int options, - sd_child_handler_t callback, + sd_event_child_handler_t callback, void *userdata, sd_event_source **ret) { @@ -862,9 +901,9 @@ int sd_event_add_child( return 0; } -int sd_event_add_defer( +_public_ int sd_event_add_defer( sd_event *e, - sd_defer_handler_t callback, + sd_event_handler_t callback, void *userdata, sd_event_source **ret) { @@ -895,9 +934,9 @@ int sd_event_add_defer( return 0; } -int sd_event_add_quit( +_public_ int sd_event_add_quit( sd_event *e, - sd_quit_handler_t callback, + sd_event_handler_t callback, void *userdata, sd_event_source **ret) { @@ -935,7 +974,7 @@ int sd_event_add_quit( return 0; } -sd_event_source* sd_event_source_ref(sd_event_source *s) { +_public_ sd_event_source* sd_event_source_ref(sd_event_source *s) { assert_return(s, NULL); assert(s->n_ref >= 1); @@ -944,8 +983,10 @@ sd_event_source* sd_event_source_ref(sd_event_source *s) { return s; } -sd_event_source* sd_event_source_unref(sd_event_source *s) { - assert_return(s, NULL); +_public_ sd_event_source* sd_event_source_unref(sd_event_source *s) { + + if (!s) + return NULL; assert(s->n_ref >= 1); s->n_ref--; @@ -956,13 +997,13 @@ sd_event_source* sd_event_source_unref(sd_event_source *s) { return NULL; } -sd_event *sd_event_get(sd_event_source *s) { +_public_ sd_event *sd_event_source_get_event(sd_event_source *s) { assert_return(s, NULL); return s->event; } -int sd_event_source_get_pending(sd_event_source *s) { +_public_ int sd_event_source_get_pending(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type != SOURCE_QUIT, -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); @@ -971,7 +1012,7 @@ int sd_event_source_get_pending(sd_event_source *s) { return s->pending; } -int sd_event_source_get_io_fd(sd_event_source *s) { +_public_ int sd_event_source_get_io_fd(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -979,7 +1020,7 @@ int sd_event_source_get_io_fd(sd_event_source *s) { return s->io.fd; } -int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) { +_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) { assert_return(s, -EINVAL); assert_return(events, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); @@ -989,12 +1030,12 @@ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) { return 0; } -int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) { +_public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) { int r; assert_return(s, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); - assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP)), -EINVAL); + assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -1008,11 +1049,12 @@ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events) { } s->io.events = events; + source_set_pending(s, false); return 0; } -int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) { +_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) { assert_return(s, -EINVAL); assert_return(revents, -EINVAL); assert_return(s->type == SOURCE_IO, -EDOM); @@ -1023,7 +1065,7 @@ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) { return 0; } -int sd_event_source_get_signal(sd_event_source *s) { +_public_ int sd_event_source_get_signal(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type == SOURCE_SIGNAL, -EDOM); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -1031,14 +1073,14 @@ int sd_event_source_get_signal(sd_event_source *s) { return s->signal.sig; } -int sd_event_source_get_priority(sd_event_source *s, int *priority) { +_public_ int sd_event_source_get_priority(sd_event_source *s, int *priority) { assert_return(s, -EINVAL); assert_return(!event_pid_changed(s->event), -ECHILD); return s->priority; } -int sd_event_source_set_priority(sd_event_source *s, int priority) { +_public_ int sd_event_source_set_priority(sd_event_source *s, int priority) { assert_return(s, -EINVAL); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -1060,7 +1102,7 @@ int sd_event_source_set_priority(sd_event_source *s, int priority) { return 0; } -int sd_event_source_get_enabled(sd_event_source *s, int *m) { +_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { assert_return(s, -EINVAL); assert_return(m, -EINVAL); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -1069,7 +1111,7 @@ int sd_event_source_get_enabled(sd_event_source *s, int *m) { return 0; } -int sd_event_source_set_enabled(sd_event_source *s, int m) { +_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { int r; assert_return(s, -EINVAL); @@ -1201,7 +1243,7 @@ int sd_event_source_set_enabled(sd_event_source *s, int m) { return 0; } -int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { +_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { assert_return(s, -EINVAL); assert_return(usec, -EINVAL); assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM); @@ -1211,18 +1253,17 @@ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) { return 0; } -int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { +_public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { assert_return(s, -EINVAL); assert_return(usec != (uint64_t) -1, -EINVAL); assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(s->event), -ECHILD); - if (s->time.next == usec) - return 0; - s->time.next = usec; + source_set_pending(s, false); + if (s->type == SOURCE_REALTIME) { prioq_reshuffle(s->event->realtime_earliest, s, &s->time.earliest_index); prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index); @@ -1234,7 +1275,7 @@ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) { return 0; } -int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) { +_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) { assert_return(s, -EINVAL); assert_return(usec, -EINVAL); assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM); @@ -1244,7 +1285,7 @@ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) { return 0; } -int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { +_public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { assert_return(s, -EINVAL); assert_return(usec != (uint64_t) -1, -EINVAL); assert_return(s->type == SOURCE_REALTIME || s->type == SOURCE_MONOTONIC, -EDOM); @@ -1254,11 +1295,10 @@ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { if (usec == 0) usec = DEFAULT_ACCURACY_USEC; - if (s->time.accuracy == usec) - return 0; - s->time.accuracy = usec; + source_set_pending(s, false); + if (s->type == SOURCE_REALTIME) prioq_reshuffle(s->event->realtime_latest, s, &s->time.latest_index); else @@ -1267,7 +1307,7 @@ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec) { return 0; } -int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { +_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { assert_return(s, -EINVAL); assert_return(pid, -EINVAL); assert_return(s->type == SOURCE_CHILD, -EDOM); @@ -1277,7 +1317,7 @@ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) { return 0; } -int sd_event_source_set_prepare(sd_event_source *s, sd_prepare_handler_t callback) { +_public_ int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback) { int r; assert_return(s, -EINVAL); @@ -1309,7 +1349,7 @@ int sd_event_source_set_prepare(sd_event_source *s, sd_prepare_handler_t callbac return 0; } -void* sd_event_source_get_userdata(sd_event_source *s) { +_public_ void* sd_event_source_get_userdata(sd_event_source *s) { assert_return(s, NULL); return s->userdata; @@ -1337,13 +1377,24 @@ static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) { dispatch as much as possible on the entire system. We implement this by waking up everywhere at the same time - within any given second if we can, synchronised via the + within any given minute if we can, synchronised via the perturbation value determined from the boot ID. If we can't, - then we try to find the same spot in every a 250ms + then we try to find the same spot in every 1s and then 250ms step. Otherwise, we pick the last possible time to wake up. */ - c = (b / USEC_PER_SEC) * USEC_PER_SEC + e->perturb; + c = (b / USEC_PER_MINUTE) * USEC_PER_MINUTE + e->perturb; + if (c >= b) { + if (_unlikely_(c < USEC_PER_MINUTE)) + return b; + + c -= USEC_PER_MINUTE; + } + + if (c >= a) + return c; + + c = (b / USEC_PER_SEC) * USEC_PER_SEC + (e->perturb % USEC_PER_SEC); if (c >= b) { if (_unlikely_(c < USEC_PER_SEC)) return b; @@ -1380,12 +1431,15 @@ static int event_arm_timer( usec_t t; int r; - assert_se(e); - assert_se(next); + assert(e); + assert(next); a = prioq_peek(earliest); if (!a || a->enabled == SD_EVENT_OFF) { + if (timer_fd < 0) + return 0; + if (*next == (usec_t) -1) return 0; @@ -1417,7 +1471,7 @@ static int event_arm_timer( r = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &its, NULL); if (r < 0) - return r; + return -errno; *next = t; return 0; @@ -1430,15 +1484,6 @@ static int process_io(sd_event *e, sd_event_source *s, uint32_t events) { s->io.revents = events; - /* - If this is a oneshot event source, then we added it to the - epoll with EPOLLONESHOT, hence we know it's not registered - anymore. We can save a syscall here... - */ - - if (s->enabled == SD_EVENT_ONESHOT) - s->io.registered = false; - return source_set_pending(s, true); } @@ -1448,7 +1493,6 @@ static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) { assert(e); assert(fd >= 0); - assert(next); assert_return(events == EPOLLIN, -EIO); @@ -1463,7 +1507,8 @@ static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) { if (ss != sizeof(x)) return -EIO; - *next = (usec_t) -1; + if (next) + *next = (usec_t) -1; return 0; } @@ -1519,6 +1564,10 @@ static int process_child(sd_event *e) { don't care about. Since this is O(n) this means that if you have a lot of processes you probably want to handle SIGCHLD yourself. + + We do not reap the children here (by using WNOWAIT), this + is only done after the event source is dispatched so that + the callback still sees the process as a zombie. */ HASHMAP_FOREACH(s, e->child_sources, i) { @@ -1531,11 +1580,27 @@ static int process_child(sd_event *e) { continue; zero(s->child.siginfo); - r = waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|s->child.options); + r = waitid(P_PID, s->child.pid, &s->child.siginfo, + WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options); if (r < 0) return -errno; if (s->child.siginfo.si_pid != 0) { + bool zombie = + s->child.siginfo.si_code == CLD_EXITED || + s->child.siginfo.si_code == CLD_KILLED || + s->child.siginfo.si_code == CLD_DUMPED; + + if (!zombie && (s->child.options & WEXITED)) { + /* If the child isn't dead then let's + * immediately remove the state change + * from the queue, since there's no + * benefit in leaving it queued */ + + assert(s->child.options & (WSTOPPED|WCONTINUED)); + waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED))); + } + r = source_set_pending(s, true); if (r < 0) return r; @@ -1589,12 +1654,11 @@ static int process_signal(sd_event *e, uint32_t events) { return r; } - return 0; } static int source_dispatch(sd_event_source *s) { - int r; + int r = 0; assert(s); assert(s->pending || s->type == SOURCE_QUIT); @@ -1611,6 +1675,8 @@ static int source_dispatch(sd_event_source *s) { return r; } + sd_event_source_ref(s); + switch (s->type) { case SOURCE_IO: @@ -1629,9 +1695,21 @@ static int source_dispatch(sd_event_source *s) { r = s->signal.callback(s, &s->signal.siginfo, s->userdata); break; - case SOURCE_CHILD: + case SOURCE_CHILD: { + bool zombie; + + zombie = s->child.siginfo.si_code == CLD_EXITED || + s->child.siginfo.si_code == CLD_KILLED || + s->child.siginfo.si_code == CLD_DUMPED; + r = s->child.callback(s, &s->child.siginfo, s->userdata); + + /* Now, reap the PID for good. */ + if (zombie) + waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED); + break; + } case SOURCE_DEFER: r = s->defer.callback(s, s->userdata); @@ -1642,6 +1720,8 @@ static int source_dispatch(sd_event_source *s) { break; } + sd_event_source_unref(s); + return r; } @@ -1711,7 +1791,44 @@ static sd_event_source* event_next_pending(sd_event *e) { return p; } -int sd_event_run(sd_event *e, uint64_t timeout) { +static int arm_watchdog(sd_event *e) { + struct itimerspec its = {}; + usec_t t; + int r; + + assert(e); + assert(e->watchdog_fd >= 0); + + t = sleep_between(e, + e->watchdog_last + (e->watchdog_period / 2), + e->watchdog_last + (e->watchdog_period * 3 / 4)); + + timespec_store(&its.it_value, t); + + r = timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL); + if (r < 0) + return -errno; + + return 0; +} + +static int process_watchdog(sd_event *e) { + assert(e); + + if (!e->watchdog) + return 0; + + /* Don't notify watchdog too often */ + if (e->watchdog_last + e->watchdog_period / 4 > e->timestamp.monotonic) + return 0; + + sd_notify(false, "WATCHDOG=1"); + e->watchdog_last = e->timestamp.monotonic; + + return arm_watchdog(e); +} + +_public_ int sd_event_run(sd_event *e, uint64_t timeout) { struct epoll_event ev_queue[EPOLL_QUEUE_MAX]; sd_event_source *p; int r, i, m; @@ -1732,18 +1849,16 @@ int sd_event_run(sd_event *e, uint64_t timeout) { if (r < 0) goto finish; - if (event_next_pending(e) || e->need_process_child) - timeout = 0; + r = event_arm_timer(e, e->monotonic_fd, e->monotonic_earliest, e->monotonic_latest, &e->monotonic_next); + if (r < 0) + goto finish; - if (timeout > 0) { - r = event_arm_timer(e, e->monotonic_fd, e->monotonic_earliest, e->monotonic_latest, &e->monotonic_next); - if (r < 0) - goto finish; + r = event_arm_timer(e, e->realtime_fd, e->realtime_earliest, e->realtime_latest, &e->realtime_next); + if (r < 0) + goto finish; - r = event_arm_timer(e, e->realtime_fd, e->realtime_earliest, e->realtime_latest, &e->realtime_next); - if (r < 0) - goto finish; - } + if (event_next_pending(e) || e->need_process_child) + timeout = 0; m = epoll_wait(e->epoll_fd, ev_queue, EPOLL_QUEUE_MAX, timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC)); @@ -1762,6 +1877,8 @@ int sd_event_run(sd_event *e, uint64_t timeout) { r = flush_timer(e, e->realtime_fd, ev_queue[i].events, &e->realtime_next); else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_SIGNAL)) r = process_signal(e, ev_queue[i].events); + else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) + r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL); else r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); @@ -1769,6 +1886,10 @@ int sd_event_run(sd_event *e, uint64_t timeout) { goto finish; } + r = process_watchdog(e); + if (r < 0) + goto finish; + r = process_timer(e, e->timestamp.monotonic, e->monotonic_earliest, e->monotonic_latest); if (r < 0) goto finish; @@ -1798,7 +1919,7 @@ finish: return r; } -int sd_event_loop(sd_event *e) { +_public_ int sd_event_loop(sd_event *e) { int r; assert_return(e, -EINVAL); @@ -1820,21 +1941,21 @@ finish: return r; } -int sd_event_get_state(sd_event *e) { +_public_ int sd_event_get_state(sd_event *e) { assert_return(e, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); return e->state; } -int sd_event_get_quit(sd_event *e) { +_public_ int sd_event_get_quit(sd_event *e) { assert_return(e, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); return e->quit_requested; } -int sd_event_request_quit(sd_event *e) { +_public_ int sd_event_request_quit(sd_event *e) { assert_return(e, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1843,7 +1964,7 @@ int sd_event_request_quit(sd_event *e) { return 0; } -int sd_event_get_now_realtime(sd_event *e, uint64_t *usec) { +_public_ int sd_event_get_now_realtime(sd_event *e, uint64_t *usec) { assert_return(e, -EINVAL); assert_return(usec, -EINVAL); assert_return(dual_timestamp_is_set(&e->timestamp), -ENODATA); @@ -1853,7 +1974,7 @@ int sd_event_get_now_realtime(sd_event *e, uint64_t *usec) { return 0; } -int sd_event_get_now_monotonic(sd_event *e, uint64_t *usec) { +_public_ int sd_event_get_now_monotonic(sd_event *e, uint64_t *usec) { assert_return(e, -EINVAL); assert_return(usec, -EINVAL); assert_return(dual_timestamp_is_set(&e->timestamp), -ENODATA); @@ -1862,3 +1983,102 @@ int sd_event_get_now_monotonic(sd_event *e, uint64_t *usec) { *usec = e->timestamp.monotonic; return 0; } + +_public_ int sd_event_default(sd_event **ret) { + + static __thread sd_event *default_event = NULL; + sd_event *e; + int r; + + if (!ret) + return !!default_event; + + if (default_event) { + *ret = sd_event_ref(default_event); + return 0; + } + + r = sd_event_new(&e); + if (r < 0) + return r; + + e->default_event_ptr = &default_event; + e->tid = gettid(); + default_event = e; + + *ret = e; + return 1; +} + +_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) { + assert_return(e, -EINVAL); + assert_return(tid, -EINVAL); + assert_return(!event_pid_changed(e), -ECHILD); + + if (e->tid != 0) { + *tid = e->tid; + return 0; + } + + return -ENXIO; +} + +_public_ int sd_event_set_watchdog(sd_event *e, int b) { + int r; + + assert_return(e, -EINVAL); + + if (e->watchdog == !!b) + return e->watchdog; + + if (b) { + struct epoll_event ev = {}; + const char *env; + + env = getenv("WATCHDOG_USEC"); + if (!env) + return false; + + r = safe_atou64(env, &e->watchdog_period); + if (r < 0) + return r; + if (e->watchdog_period <= 0) + return -EIO; + + /* Issue first ping immediately */ + sd_notify(false, "WATCHDOG=1"); + e->watchdog_last = now(CLOCK_MONOTONIC); + + e->watchdog_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); + if (e->watchdog_fd < 0) + return -errno; + + r = arm_watchdog(e); + if (r < 0) + goto fail; + + ev.events = EPOLLIN; + ev.data.ptr = INT_TO_PTR(SOURCE_WATCHDOG); + + r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->watchdog_fd, &ev); + if (r < 0) { + r = -errno; + goto fail; + } + + } else { + if (e->watchdog_fd >= 0) { + epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL); + close_nointr_nofail(e->watchdog_fd); + e->watchdog_fd = -1; + } + } + + e->watchdog = !!b; + return e->watchdog; + +fail: + close_nointr_nofail(e->watchdog_fd); + e->watchdog_fd = -1; + return r; +}