sd-event.c revision 8046c4576a68977a1089d2585866bfab8152661b
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#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 "missing.h"
#include "set.h"
#include "list.h"
#include "signal-util.h"
#include "sd-event.h"
typedef enum EventSourceType {
/* All objects we use in epoll events start with this value, so that
* we know how to dispatch it */
typedef enum WakeupType {
_WAKEUP_TYPE_INVALID = -1,
} WakeupType;
#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM)
struct sd_event_source {
unsigned n_ref;
void *userdata;
char *description;
int enabled:3;
bool pending:1;
bool dispatching:1;
bool floating:1;
unsigned pending_index;
unsigned prepare_index;
unsigned pending_iteration;
unsigned prepare_iteration;
union {
struct {
int fd;
bool registered:1;
} io;
struct {
unsigned earliest_index;
unsigned latest_index;
} time;
struct {
struct signalfd_siginfo siginfo;
int sig;
} signal;
struct {
int options;
} child;
struct {
} defer;
struct {
} post;
struct {
unsigned prioq_index;
} exit;
};
};
struct clock_data {
int fd;
/* For all clocks we maintain two priority queues each, one
* ordered for the earliest times the events may be
* dispatched, and one ordered by the latest times they must
* have been dispatched. The range between the top entries in
* the two prioqs is the time window we can freely schedule
* wakeups in */
bool needs_rearm:1;
};
struct signal_data {
/* For each priority we maintain one signal fd, so that we
* only have to dequeue a single event per priority at a
* time. */
int fd;
};
struct sd_event {
unsigned n_ref;
int epoll_fd;
int watchdog_fd;
/* timerfd_create() only supports these five clocks so far. We
* can add support for more clocks when the kernel learns to
* deal with them, too. */
struct clock_data realtime;
struct clock_data boottime;
struct clock_data monotonic;
struct clock_data realtime_alarm;
struct clock_data boottime_alarm;
unsigned n_enabled_child_sources;
unsigned iteration;
int state;
bool exit_requested:1;
bool need_process_child:1;
bool watchdog:1;
int exit_code;
unsigned n_sources;
};
static void source_disconnect(sd_event_source *s);
static int pending_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
/* Enabled ones first */
return -1;
return 1;
/* Lower priority values first */
return -1;
return 1;
/* Older entries first */
if (x->pending_iteration < y->pending_iteration)
return -1;
if (x->pending_iteration > y->pending_iteration)
return 1;
/* Stability for the rest */
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
static int prepare_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
/* Enabled ones first */
return -1;
return 1;
/* Move most recently prepared ones last, so that we can stop
* preparing as soon as we hit one that has already been
* prepared in the current iteration */
if (x->prepare_iteration < y->prepare_iteration)
return -1;
if (x->prepare_iteration > y->prepare_iteration)
return 1;
/* Lower priority values first */
return -1;
return 1;
/* Stability for the rest */
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
static int earliest_time_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
/* Enabled ones first */
return -1;
return 1;
/* Move the pending ones to the end */
return -1;
return 1;
/* Order by time */
return -1;
return 1;
/* Stability for the rest */
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
static int latest_time_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
/* Enabled ones first */
return -1;
return 1;
/* Move the pending ones to the end */
return -1;
return 1;
/* Order by time */
return -1;
return 1;
/* Stability for the rest */
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
static int exit_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
/* Enabled ones first */
return -1;
return 1;
/* Lower priority values first */
return -1;
return 1;
/* Stability for the rest */
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
static void free_clock_data(struct clock_data *d) {
assert(d);
safe_close(d->fd);
prioq_free(d->earliest);
prioq_free(d->latest);
}
static void event_free(sd_event *e) {
sd_event_source *s;
assert(e);
while ((s = e->sources)) {
}
if (e->default_event_ptr)
*(e->default_event_ptr) = NULL;
safe_close(e->epoll_fd);
safe_close(e->watchdog_fd);
free_clock_data(&e->realtime);
free_clock_data(&e->boottime);
free_clock_data(&e->monotonic);
prioq_free(e->pending);
prioq_free(e->prepare);
prioq_free(e->exit);
free(e->signal_sources);
hashmap_free(e->signal_data);
set_free(e->post_sources);
free(e);
}
sd_event *e;
int r;
if (!e)
return -ENOMEM;
e->n_ref = 1;
e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1;
e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY;
e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA;
e->original_pid = getpid();
e->perturb = USEC_INFINITY;
if (!e->pending) {
r = -ENOMEM;
goto fail;
}
if (e->epoll_fd < 0) {
r = -errno;
goto fail;
}
*ret = e;
return 0;
fail:
event_free(e);
return r;
}
assert_return(e, NULL);
e->n_ref++;
return e;
}
if (!e)
return NULL;
e->n_ref--;
if (e->n_ref <= 0)
event_free(e);
return NULL;
}
static bool event_pid_changed(sd_event *e) {
assert(e);
/* We don't support people creating an event loop and keeping
* it around over a fork(). Let's complain. */
return e->original_pid != getpid();
}
static void source_io_unregister(sd_event_source *s) {
int r;
assert(s);
if (event_pid_changed(s->event))
return;
if (!s->io.registered)
return;
if (r < 0)
s->io.registered = false;
}
static int source_io_register(
sd_event_source *s,
int enabled,
struct epoll_event ev = {};
int r;
assert(s);
if (enabled == SD_EVENT_ONESHOT)
if (s->io.registered)
else
if (r < 0)
return -errno;
s->io.registered = true;
return 0;
}
switch (t) {
case SOURCE_TIME_REALTIME:
return CLOCK_REALTIME;
case SOURCE_TIME_BOOTTIME:
return CLOCK_BOOTTIME;
case SOURCE_TIME_MONOTONIC:
return CLOCK_MONOTONIC;
return CLOCK_REALTIME_ALARM;
return CLOCK_BOOTTIME_ALARM;
default:
return (clockid_t) -1;
}
}
switch (clock) {
case CLOCK_REALTIME:
return SOURCE_TIME_REALTIME;
case CLOCK_BOOTTIME:
return SOURCE_TIME_BOOTTIME;
case CLOCK_MONOTONIC:
return SOURCE_TIME_MONOTONIC;
case CLOCK_REALTIME_ALARM:
return SOURCE_TIME_REALTIME_ALARM;
case CLOCK_BOOTTIME_ALARM:
return SOURCE_TIME_BOOTTIME_ALARM;
default:
}
}
assert(e);
switch (t) {
case SOURCE_TIME_REALTIME:
return &e->realtime;
case SOURCE_TIME_BOOTTIME:
return &e->boottime;
case SOURCE_TIME_MONOTONIC:
return &e->monotonic;
return &e->realtime_alarm;
return &e->boottime_alarm;
default:
return NULL;
}
}
static int event_make_signal_data(
sd_event *e,
int sig,
struct signal_data **ret) {
struct epoll_event ev = {};
struct signal_data *d;
bool added = false;
int r;
assert(e);
if (event_pid_changed(e))
return -ECHILD;
else
priority = 0;
if (d) {
if (ret)
*ret = d;
return 0;
}
} else {
if (r < 0)
return r;
if (!d)
return -ENOMEM;
d->wakeup = WAKEUP_SIGNAL_DATA;
d->fd = -1;
if (r < 0)
return r;
added = true;
}
if (r < 0) {
r = -errno;
goto fail;
}
if (d->fd >= 0) {
if (ret)
*ret = d;
return 0;
}
d->fd = r;
if (r < 0) {
r = -errno;
goto fail;
}
if (ret)
*ret = d;
return 0;
fail:
if (added) {
free(d);
}
return r;
}
assert(e);
assert(d);
/* Turns off the specified signal in the signal data
* object. If the signal mask of the object becomes empty that
* way removes it. */
return;
if (sigisemptyset(&d->sigset)) {
/* If all the mask is all-zero we can get rid of the structure */
safe_close(d->fd);
free(d);
return;
}
}
struct signal_data *d;
static const int64_t zero_priority = 0;
assert(e);
/* Rechecks if the specified signal is still something we are
* interested in. If not, we'll unmask it, and possibly drop
* the signalfd for it. */
e->n_enabled_child_sources > 0)
return;
if (e->signal_sources &&
e->signal_sources[sig] &&
return;
/*
* The specified signal might be enabled in three different queues:
*
* 1) the one that belongs to the priority passed (if it is non-NULL)
* 2) the one that belongs to the priority of the event source of the signal (if there is one)
* 3) the 0 priority (to cover the SIGCHLD case)
*
* Hence, let's remove it from all three here.
*/
if (priority) {
if (d)
event_unmask_signal_data(e, d, sig);
}
if (d)
event_unmask_signal_data(e, d, sig);
}
if (d)
event_unmask_signal_data(e, d, sig);
}
static void source_disconnect(sd_event_source *s) {
assert(s);
if (!s->event)
return;
switch (s->type) {
case SOURCE_IO:
break;
case SOURCE_TIME_REALTIME:
case SOURCE_TIME_BOOTTIME:
case SOURCE_TIME_MONOTONIC:
case SOURCE_TIME_BOOTTIME_ALARM: {
struct clock_data *d;
assert(d);
d->needs_rearm = true;
break;
}
case SOURCE_SIGNAL:
if (s->event->signal_sources)
}
break;
case SOURCE_CHILD:
if (s->enabled != SD_EVENT_OFF) {
s->event->n_enabled_child_sources--;
}
}
break;
case SOURCE_DEFER:
/* nothing */
break;
case SOURCE_POST:
break;
case SOURCE_EXIT:
break;
default:
assert_not_reached("Wut? I shouldn't exist.");
}
if (s->pending)
if (s->prepare)
if (!s->floating)
}
static void source_free(sd_event_source *s) {
assert(s);
free(s->description);
free(s);
}
static int source_set_pending(sd_event_source *s, bool b) {
int r;
assert(s);
if (s->pending == b)
return 0;
s->pending = b;
if (b) {
if (r < 0) {
s->pending = false;
return r;
}
} else
if (EVENT_SOURCE_IS_TIME(s->type)) {
struct clock_data *d;
assert(d);
d->needs_rearm = true;
}
if (s->type == SOURCE_SIGNAL && !b) {
struct signal_data *d;
if (d && d->current == s)
}
return 0;
}
sd_event_source *s;
assert(e);
if (!s)
return NULL;
s->n_ref = 1;
s->event = e;
if (!floating)
sd_event_ref(e);
e->n_sources ++;
return s;
}
_public_ int sd_event_add_io(
sd_event *e,
int fd,
void *userdata) {
sd_event_source *s;
int r;
assert_return(e, -EINVAL);
assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
if (!s)
return -ENOMEM;
s->wakeup = WAKEUP_EVENT_SOURCE;
s->enabled = SD_EVENT_ON;
if (r < 0) {
source_free(s);
return r;
}
if (ret)
*ret = s;
return 0;
}
static void initialize_perturb(sd_event *e) {
sd_id128_t bootid = {};
/* When we sleep for longer, we try to realign the wakeup to
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. */
return;
if (sd_id128_get_boot(&bootid) >= 0)
}
static int event_setup_timer_fd(
sd_event *e,
struct clock_data *d,
struct epoll_event ev = {};
int r, fd;
assert(e);
assert(d);
return 0;
if (fd < 0)
return -errno;
if (r < 0) {
safe_close(fd);
return -errno;
}
return 0;
}
assert(s);
}
sd_event *e,
void *userdata) {
sd_event_source *s;
struct clock_data *d;
int r;
assert_return(e, -EINVAL);
if (!callback)
d = event_get_clock_data(e, type);
assert(d);
if (!d->earliest) {
if (!d->earliest)
return -ENOMEM;
}
if (!d->latest) {
if (!d->latest)
return -ENOMEM;
}
if (d->fd < 0) {
r = event_setup_timer_fd(e, d, clock);
if (r < 0)
return r;
}
if (!s)
return -ENOMEM;
s->enabled = SD_EVENT_ONESHOT;
d->needs_rearm = true;
if (r < 0)
goto fail;
if (r < 0)
goto fail;
if (ret)
*ret = s;
return 0;
fail:
source_free(s);
return r;
}
static int signal_exit_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
assert(s);
}
sd_event *e,
int sig,
void *userdata) {
sd_event_source *s;
struct signal_data *d;
int r;
assert_return(e, -EINVAL);
if (!callback)
if (r < 0)
return -errno;
return -EBUSY;
if (!e->signal_sources) {
if (!e->signal_sources)
return -ENOMEM;
} else if (e->signal_sources[sig])
return -EBUSY;
if (!s)
return -ENOMEM;
s->enabled = SD_EVENT_ON;
e->signal_sources[sig] = s;
r = event_make_signal_data(e, sig, &d);
if (r < 0) {
source_free(s);
return r;
}
/* Use the signal name as description for the event source by default */
if (ret)
*ret = s;
return 0;
}
sd_event *e,
int options,
void *userdata) {
sd_event_source *s;
int r;
assert_return(e, -EINVAL);
if (r < 0)
return r;
return -EBUSY;
if (!s)
return -ENOMEM;
s->enabled = SD_EVENT_ONESHOT;
if (r < 0) {
source_free(s);
return r;
}
e->n_enabled_child_sources ++;
if (r < 0) {
e->n_enabled_child_sources--;
source_free(s);
return r;
}
e->need_process_child = true;
if (ret)
*ret = s;
return 0;
}
sd_event *e,
void *userdata) {
sd_event_source *s;
int r;
assert_return(e, -EINVAL);
if (!s)
return -ENOMEM;
s->enabled = SD_EVENT_ONESHOT;
r = source_set_pending(s, true);
if (r < 0) {
source_free(s);
return r;
}
if (ret)
*ret = s;
return 0;
}
sd_event *e,
void *userdata) {
sd_event_source *s;
int r;
assert_return(e, -EINVAL);
if (r < 0)
return r;
if (!s)
return -ENOMEM;
s->enabled = SD_EVENT_ON;
r = set_put(e->post_sources, s);
if (r < 0) {
source_free(s);
return r;
}
if (ret)
*ret = s;
return 0;
}
sd_event *e,
void *userdata) {
sd_event_source *s;
int r;
assert_return(e, -EINVAL);
if (!e->exit) {
if (!e->exit)
return -ENOMEM;
}
if (!s)
return -ENOMEM;
s->enabled = SD_EVENT_ONESHOT;
if (r < 0) {
source_free(s);
return r;
}
if (ret)
*ret = s;
return 0;
}
assert_return(s, NULL);
s->n_ref++;
return s;
}
if (!s)
return NULL;
s->n_ref--;
if (s->n_ref <= 0) {
/* Here's a special hack: when we are called from a
* dispatch handler we won't free the event source
* immediately, but we will detach the fd from the
* epoll. This way it is safe for the caller to unref
* the event source and immediately close the fd, but
* we still retain a valid event source object after
* the callback. */
if (s->dispatching) {
} else
source_free(s);
}
return NULL;
}
assert_return(s, -EINVAL);
}
assert_return(s, -EINVAL);
*description = s->description;
return 0;
}
assert_return(s, NULL);
return s->event;
}
assert_return(s, -EINVAL);
return s->pending;
}
assert_return(s, -EINVAL);
}
int r;
assert_return(s, -EINVAL);
return 0;
if (s->enabled == SD_EVENT_OFF) {
s->io.registered = false;
} else {
int saved_fd;
s->io.registered = false;
if (r < 0) {
s->io.registered = true;
return r;
}
}
return 0;
}
assert_return(s, -EINVAL);
return 0;
}
int r;
assert_return(s, -EINVAL);
assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL);
/* edge-triggered updates are never skipped, so we can reset edges */
return 0;
if (s->enabled != SD_EVENT_OFF) {
if (r < 0)
return r;
}
source_set_pending(s, false);
return 0;
}
assert_return(s, -EINVAL);
return 0;
}
assert_return(s, -EINVAL);
}
assert_return(s, -EINVAL);
return s->priority;
}
int r;
assert_return(s, -EINVAL);
return 0;
struct signal_data *old, *d;
/* Move us from the signalfd belonging to the old
* priority to the signalfd of the new priority */
if (r < 0) {
return r;
}
} else
if (s->pending)
if (s->prepare)
if (s->type == SOURCE_EXIT)
return 0;
}
assert_return(s, -EINVAL);
assert_return(m, -EINVAL);
*m = s->enabled;
return 0;
}
int r;
assert_return(s, -EINVAL);
/* If we are dead anyway, we are fine with turning off
* sources, but everything else needs to fail. */
return m == SD_EVENT_OFF ? 0 : -ESTALE;
if (s->enabled == m)
return 0;
if (m == SD_EVENT_OFF) {
switch (s->type) {
case SOURCE_IO:
s->enabled = m;
break;
case SOURCE_TIME_REALTIME:
case SOURCE_TIME_BOOTTIME:
case SOURCE_TIME_MONOTONIC:
case SOURCE_TIME_BOOTTIME_ALARM: {
struct clock_data *d;
s->enabled = m;
assert(d);
d->needs_rearm = true;
break;
}
case SOURCE_SIGNAL:
s->enabled = m;
break;
case SOURCE_CHILD:
s->enabled = m;
s->event->n_enabled_child_sources--;
break;
case SOURCE_EXIT:
s->enabled = m;
break;
case SOURCE_DEFER:
case SOURCE_POST:
s->enabled = m;
break;
default:
assert_not_reached("Wut? I shouldn't exist.");
}
} else {
switch (s->type) {
case SOURCE_IO:
if (r < 0)
return r;
s->enabled = m;
break;
case SOURCE_TIME_REALTIME:
case SOURCE_TIME_BOOTTIME:
case SOURCE_TIME_MONOTONIC:
case SOURCE_TIME_BOOTTIME_ALARM: {
struct clock_data *d;
s->enabled = m;
assert(d);
d->needs_rearm = true;
break;
}
case SOURCE_SIGNAL:
s->enabled = m;
if (r < 0) {
s->enabled = SD_EVENT_OFF;
return r;
}
break;
case SOURCE_CHILD:
if (s->enabled == SD_EVENT_OFF)
s->event->n_enabled_child_sources++;
s->enabled = m;
if (r < 0) {
s->enabled = SD_EVENT_OFF;
s->event->n_enabled_child_sources--;
return r;
}
break;
case SOURCE_EXIT:
s->enabled = m;
break;
case SOURCE_DEFER:
case SOURCE_POST:
s->enabled = m;
break;
default:
assert_not_reached("Wut? I shouldn't exist.");
}
}
if (s->pending)
if (s->prepare)
return 0;
}
assert_return(s, -EINVAL);
return 0;
}
struct clock_data *d;
assert_return(s, -EINVAL);
source_set_pending(s, false);
assert(d);
d->needs_rearm = true;
return 0;
}
assert_return(s, -EINVAL);
return 0;
}
struct clock_data *d;
assert_return(s, -EINVAL);
if (usec == 0)
source_set_pending(s, false);
assert(d);
d->needs_rearm = true;
return 0;
}
assert_return(s, -EINVAL);
return 0;
}
assert_return(s, -EINVAL);
return 0;
}
int r;
assert_return(s, -EINVAL);
return 0;
return 0;
}
if (r < 0)
return r;
if (callback) {
if (r < 0)
return r;
} else
return 0;
}
assert_return(s, NULL);
return s->userdata;
}
void *ret;
assert_return(s, NULL);
return ret;
}
usec_t c;
assert(e);
assert(a <= b);
if (a <= 0)
return 0;
if (b <= a + 1)
return a;
/*
Find a good time to wake up again between times a and b. We
have two goals here:
a) We want to wake up as seldom as possible, hence prefer
later times over earlier times.
b) But if we have to wake up, then let's make sure to
dispatch as much as possible on the entire system.
We implement this by waking up everywhere at the same time
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 10s, then 1s and
then 250ms step. Otherwise, we pick the last possible time
to wake up.
*/
if (c >= b) {
if (_unlikely_(c < USEC_PER_MINUTE))
return b;
c -= USEC_PER_MINUTE;
}
if (c >= a)
return c;
if (c >= b) {
return b;
c -= USEC_PER_SEC*10;
}
if (c >= a)
return c;
if (c >= b) {
if (_unlikely_(c < USEC_PER_SEC))
return b;
c -= USEC_PER_SEC;
}
if (c >= a)
return c;
if (c >= b) {
return b;
c -= USEC_PER_MSEC*250;
}
if (c >= a)
return c;
return b;
}
static int event_arm_timer(
sd_event *e,
struct clock_data *d) {
struct itimerspec its = {};
sd_event_source *a, *b;
usec_t t;
int r;
assert(e);
assert(d);
if (!d->needs_rearm)
return 0;
else
d->needs_rearm = false;
a = prioq_peek(d->earliest);
if (!a || a->enabled == SD_EVENT_OFF) {
if (d->fd < 0)
return 0;
if (d->next == USEC_INFINITY)
return 0;
/* disarm */
if (r < 0)
return r;
d->next = USEC_INFINITY;
return 0;
}
b = prioq_peek(d->latest);
if (d->next == t)
return 0;
if (t == 0) {
/* We don' want to disarm here, just mean some time looooong ago. */
} else
if (r < 0)
return -errno;
d->next = t;
return 0;
}
assert(e);
assert(s);
/* If the event source was already pending, we just OR in the
* new revents, otherwise we reset the value. The ORing is
* necessary to handle EPOLLONESHOT events properly where
* readability might happen independently of writability, and
* we need to keep track of both */
if (s->pending)
else
return source_set_pending(s, true);
}
uint64_t x;
assert(e);
if (ss < 0) {
return 0;
return -errno;
}
if (_unlikely_(ss != sizeof(x)))
return -EIO;
if (next)
*next = USEC_INFINITY;
return 0;
}
static int process_timer(
sd_event *e,
usec_t n,
struct clock_data *d) {
sd_event_source *s;
int r;
assert(e);
assert(d);
for (;;) {
s = prioq_peek(d->earliest);
if (!s ||
s->enabled == SD_EVENT_OFF ||
s->pending)
break;
r = source_set_pending(s, true);
if (r < 0)
return r;
d->needs_rearm = true;
}
return 0;
}
static int process_child(sd_event *e) {
sd_event_source *s;
Iterator i;
int r;
assert(e);
e->need_process_child = false;
/*
So, this is ugly. We iteratively invoke waitid() with P_PID
+ WNOHANG for each PID we wait for, instead of using
P_ALL. This is because we only want to get child
information of very specific child processes, and not all
of them. We might not have processed the SIGCHLD even of a
previous invocation and we don't want to maintain a
unbounded *per-child* event queue, hence we really don't
want anything flushed out of the kernel's queue that we
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) {
if (s->pending)
continue;
if (s->enabled == SD_EVENT_OFF)
continue;
if (r < 0)
return -errno;
bool zombie =
/* 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 */
}
r = source_set_pending(s, true);
if (r < 0)
return r;
}
}
return 0;
}
bool read_one = false;
int r;
assert(e);
/* If there's a signal queued on this priority and SIGCHLD is
on this priority too, then make sure to recheck the
children we watch. This is because we only ever dequeue
the first signal per priority, and if we dequeue one, and
SIGCHLD might be enqueued later we wouldn't know, but we
might have higher priority children we care about hence we
need to check that explicitly. */
e->need_process_child = true;
/* If there's already an event source pending for this
* priority we don't read another */
if (d->current)
return 0;
for (;;) {
struct signalfd_siginfo si;
ssize_t n;
sd_event_source *s = NULL;
if (n < 0) {
return read_one;
return -errno;
}
if (_unlikely_(n != sizeof(si)))
return -EIO;
read_one = true;
if (e->signal_sources)
if (!s)
continue;
if (s->pending)
continue;
d->current = s;
r = source_set_pending(s, true);
if (r < 0)
return r;
return 1;
}
}
static int source_dispatch(sd_event_source *s) {
int r = 0;
assert(s);
r = source_set_pending(s, false);
if (r < 0)
return r;
}
if (s->type != SOURCE_POST) {
sd_event_source *z;
Iterator i;
/* If we execute a non-post source, let's mark all
* post sources as pending */
if (z->enabled == SD_EVENT_OFF)
continue;
r = source_set_pending(z, true);
if (r < 0)
return r;
}
}
if (s->enabled == SD_EVENT_ONESHOT) {
r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
if (r < 0)
return r;
}
s->dispatching = true;
switch (s->type) {
case SOURCE_IO:
break;
case SOURCE_TIME_REALTIME:
case SOURCE_TIME_BOOTTIME:
case SOURCE_TIME_MONOTONIC:
break;
case SOURCE_SIGNAL:
break;
case SOURCE_CHILD: {
bool zombie;
/* Now, reap the PID for good. */
if (zombie)
break;
}
case SOURCE_DEFER:
break;
case SOURCE_POST:
break;
case SOURCE_EXIT:
break;
case SOURCE_WATCHDOG:
assert_not_reached("Wut? I shouldn't exist.");
}
s->dispatching = false;
if (r < 0) {
if (s->description)
else
log_debug_errno(r, "Event source %p returned error, disabling: %m", s);
}
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
return 1;
}
static int event_prepare(sd_event *e) {
int r;
assert(e);
for (;;) {
sd_event_source *s;
s = prioq_peek(e->prepare);
break;
s->prepare_iteration = e->iteration;
if (r < 0)
return r;
s->dispatching = true;
s->dispatching = false;
if (r < 0) {
if (s->description)
log_debug_errno(r, "Prepare callback of event source '%s' returned error, disabling: %m", s->description);
else
log_debug_errno(r, "Prepare callback of event source %p returned error, disabling: %m", s);
}
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
}
return 0;
}
static int dispatch_exit(sd_event *e) {
sd_event_source *p;
int r;
assert(e);
p = prioq_peek(e->exit);
if (!p || p->enabled == SD_EVENT_OFF) {
e->state = SD_EVENT_FINISHED;
return 0;
}
sd_event_ref(e);
e->iteration++;
e->state = SD_EVENT_EXITING;
r = source_dispatch(p);
e->state = SD_EVENT_INITIAL;
sd_event_unref(e);
return r;
}
sd_event_source *p;
assert(e);
p = prioq_peek(e->pending);
if (!p)
return NULL;
if (p->enabled == SD_EVENT_OFF)
return NULL;
return p;
}
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,
/* Make sure we never set the watchdog to 0, which tells the
* kernel to disable it. */
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 */
return 0;
sd_notify(false, "WATCHDOG=1");
return arm_watchdog(e);
}
int r;
assert_return(e, -EINVAL);
if (e->exit_requested)
goto pending;
e->iteration++;
r = event_prepare(e);
if (r < 0)
return r;
r = event_arm_timer(e, &e->realtime);
if (r < 0)
return r;
r = event_arm_timer(e, &e->boottime);
if (r < 0)
return r;
r = event_arm_timer(e, &e->monotonic);
if (r < 0)
return r;
r = event_arm_timer(e, &e->realtime_alarm);
if (r < 0)
return r;
r = event_arm_timer(e, &e->boottime_alarm);
if (r < 0)
return r;
if (event_next_pending(e) || e->need_process_child)
goto pending;
e->state = SD_EVENT_ARMED;
return 0;
e->state = SD_EVENT_ARMED;
r = sd_event_wait(e, 0);
if (r == 0)
e->state = SD_EVENT_ARMED;
return r;
}
struct epoll_event *ev_queue;
unsigned ev_queue_max;
int r, m, i;
assert_return(e, -EINVAL);
if (e->exit_requested) {
e->state = SD_EVENT_PENDING;
return 1;
}
if (m < 0) {
e->state = SD_EVENT_PENDING;
return 1;
}
r = -errno;
goto finish;
}
dual_timestamp_get(&e->timestamp);
for (i = 0; i < m; i++) {
else {
switch (*t) {
case WAKEUP_EVENT_SOURCE:
break;
case WAKEUP_CLOCK_DATA: {
break;
}
case WAKEUP_SIGNAL_DATA:
break;
default:
assert_not_reached("Invalid wake-up pointer");
}
}
if (r < 0)
goto finish;
}
r = process_watchdog(e);
if (r < 0)
goto finish;
if (r < 0)
goto finish;
if (r < 0)
goto finish;
if (r < 0)
goto finish;
if (r < 0)
goto finish;
if (r < 0)
goto finish;
if (e->need_process_child) {
r = process_child(e);
if (r < 0)
goto finish;
}
if (event_next_pending(e)) {
e->state = SD_EVENT_PENDING;
return 1;
}
r = 0;
e->state = SD_EVENT_INITIAL;
return r;
}
sd_event_source *p;
int r;
assert_return(e, -EINVAL);
if (e->exit_requested)
return dispatch_exit(e);
p = event_next_pending(e);
if (p) {
sd_event_ref(e);
e->state = SD_EVENT_RUNNING;
r = source_dispatch(p);
e->state = SD_EVENT_INITIAL;
sd_event_unref(e);
return r;
}
e->state = SD_EVENT_INITIAL;
return 1;
}
int r;
assert_return(e, -EINVAL);
r = sd_event_prepare(e);
if (r == 0)
/* There was nothing? Then wait... */
r = sd_event_wait(e, timeout);
if (r > 0) {
/* There's something now, then let's dispatch it */
r = sd_event_dispatch(e);
if (r < 0)
return r;
return 1;
}
return r;
}
int r;
assert_return(e, -EINVAL);
sd_event_ref(e);
while (e->state != SD_EVENT_FINISHED) {
if (r < 0)
goto finish;
}
r = e->exit_code;
sd_event_unref(e);
return r;
}
assert_return(e, -EINVAL);
return e->epoll_fd;
}
assert_return(e, -EINVAL);
return e->state;
}
assert_return(e, -EINVAL);
if (!e->exit_requested)
return -ENODATA;
return 0;
}
assert_return(e, -EINVAL);
e->exit_requested = true;
return 0;
}
assert_return(e, -EINVAL);
if (!dual_timestamp_is_set(&e->timestamp)) {
/* Implicitly fall back to now() if we never ran
* before and thus have no cached time. */
return 1;
}
switch (clock) {
case CLOCK_REALTIME:
case CLOCK_REALTIME_ALARM:
break;
case CLOCK_MONOTONIC:
break;
case CLOCK_BOOTTIME:
case CLOCK_BOOTTIME_ALARM:
*usec = e->timestamp_boottime;
break;
}
return 0;
}
int r;
if (!ret)
return !!default_event;
if (default_event) {
return 0;
}
r = sd_event_new(&e);
if (r < 0)
return r;
e->default_event_ptr = &default_event;
default_event = e;
*ret = e;
return 1;
}
assert_return(e, -EINVAL);
if (e->tid != 0) {
return 0;
}
return -ENXIO;
}
int r;
assert_return(e, -EINVAL);
if (e->watchdog == !!b)
return e->watchdog;
if (b) {
struct epoll_event ev = {};
r = sd_watchdog_enabled(false, &e->watchdog_period);
if (r <= 0)
return r;
/* Issue first ping immediately */
sd_notify(false, "WATCHDOG=1");
if (e->watchdog_fd < 0)
return -errno;
r = arm_watchdog(e);
if (r < 0)
goto fail;
if (r < 0) {
r = -errno;
goto fail;
}
} else {
if (e->watchdog_fd >= 0) {
}
}
e->watchdog = !!b;
return e->watchdog;
fail:
return r;
}
assert_return(e, -EINVAL);
return e->watchdog;
}