Systemd/src/libsystemd-bus/test-event.c
Lennart Poettering 12179984a3 event: when unreffing an event source from its own handler, detach fd from epoll
The pattern of unreffing an IO event source and then closing its fd is
frequently seen in even source callbacks. Previously this likely
resultet in us removing the fd from the epoll after it was closed which
is problematic, since while we were dispatching we always kept an extra
reference to event source objects because we might still need it later.
2013-12-13 04:06:43 +01:00

249 lines
7.8 KiB
C

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
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-event.h"
#include "log.h"
#include "util.h"
static int prepare_handler(sd_event_source *s, void *userdata) {
log_info("preparing %c", PTR_TO_INT(userdata));
return 1;
}
static bool got_a, got_b, got_c, got_unref;
static unsigned got_d;
static int unref_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_event_source_unref(s);
got_unref = true;
return 0;
}
static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
log_info("got IO on %c", PTR_TO_INT(userdata));
if (userdata == INT_TO_PTR('a')) {
assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
assert_se(!got_a);
got_a = true;
} else if (userdata == INT_TO_PTR('b')) {
assert_se(!got_b);
got_b = true;
} else if (userdata == INT_TO_PTR('d')) {
got_d++;
if (got_d < 2)
assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
else
assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
} else
assert_not_reached("Yuck!");
return 1;
}
static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
assert(s);
assert(si);
log_info("got child on %c", PTR_TO_INT(userdata));
assert(userdata == INT_TO_PTR('f'));
assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
sd_event_source_unref(s);
return 1;
}
static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
sd_event_source *p;
sigset_t ss;
pid_t pid;
assert(s);
assert(si);
log_info("got signal on %c", PTR_TO_INT(userdata));
assert(userdata == INT_TO_PTR('e'));
assert_se(sigemptyset(&ss) >= 0);
assert_se(sigaddset(&ss, SIGCHLD) >= 0);
assert_se(sigprocmask(SIG_BLOCK, &ss, NULL) >= 0);
pid = fork();
assert_se(pid >= 0);
if (pid == 0)
_exit(0);
assert_se(sd_event_add_child(sd_event_source_get_event(s), pid, WEXITED, child_handler, INT_TO_PTR('f'), &p) >= 0);
assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
sd_event_source_unref(s);
return 1;
}
static int defer_handler(sd_event_source *s, void *userdata) {
sd_event_source *p;
sigset_t ss;
assert(s);
log_info("got defer on %c", PTR_TO_INT(userdata));
assert(userdata == INT_TO_PTR('d'));
assert_se(sigemptyset(&ss) >= 0);
assert_se(sigaddset(&ss, SIGUSR1) >= 0);
assert_se(sigprocmask(SIG_BLOCK, &ss, NULL) >= 0);
assert_se(sd_event_add_signal(sd_event_source_get_event(s), SIGUSR1, signal_handler, INT_TO_PTR('e'), &p) >= 0);
assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
raise(SIGUSR1);
sd_event_source_unref(s);
return 1;
}
static bool do_quit = false;
static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
log_info("got timer on %c", PTR_TO_INT(userdata));
if (userdata == INT_TO_PTR('c')) {
if (do_quit) {
sd_event_source *p;
assert_se(sd_event_add_defer(sd_event_source_get_event(s), defer_handler, INT_TO_PTR('d'), &p) >= 0);
assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
} else {
assert(!got_c);
got_c = true;
}
} else
assert_not_reached("Huh?");
return 2;
}
static bool got_exit = false;
static int exit_handler(sd_event_source *s, void *userdata) {
log_info("got quit handler on %c", PTR_TO_INT(userdata));
got_exit = true;
return 3;
}
int main(int argc, char *argv[]) {
sd_event *e = NULL;
sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
static const char ch = 'x';
int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 };
assert_se(pipe(a) >= 0);
assert_se(pipe(b) >= 0);
assert_se(pipe(d) >= 0);
assert_se(pipe(k) >= 0);
assert_se(sd_event_default(&e) >= 0);
assert_se(sd_event_set_watchdog(e, true) >= 0);
/* Test whether we cleanly can destroy an io event source from its own handler */
got_unref = false;
assert_se(sd_event_add_io(e, k[0], EPOLLIN, unref_handler, NULL, &t) >= 0);
assert_se(write(k[1], &ch, 1) == 1);
assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
assert_se(got_unref);
got_a = false, got_b = false, got_c = false, got_d = 0;
/* Add a oneshot handler, trigger it, re-enable it, and trigger
* it again. */
assert_se(sd_event_add_io(e, d[0], EPOLLIN, io_handler, INT_TO_PTR('d'), &w) >= 0);
assert_se(sd_event_source_set_enabled(w, SD_EVENT_ONESHOT) >= 0);
assert_se(write(d[1], &ch, 1) >= 0);
assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
assert_se(got_d == 1);
assert_se(write(d[1], &ch, 1) >= 0);
assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
assert_se(got_d == 2);
assert_se(sd_event_add_io(e, a[0], EPOLLIN, io_handler, INT_TO_PTR('a'), &x) >= 0);
assert_se(sd_event_add_io(e, b[0], EPOLLIN, io_handler, INT_TO_PTR('b'), &y) >= 0);
assert_se(sd_event_add_monotonic(e, 0, 0, time_handler, INT_TO_PTR('c'), &z) >= 0);
assert_se(sd_event_add_exit(e, exit_handler, INT_TO_PTR('g'), &q) >= 0);
assert_se(sd_event_source_set_priority(x, 99) >= 0);
assert_se(sd_event_source_set_enabled(y, SD_EVENT_ONESHOT) >= 0);
assert_se(sd_event_source_set_prepare(x, prepare_handler) >= 0);
assert_se(sd_event_source_set_priority(z, 50) >= 0);
assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
assert_se(sd_event_source_set_prepare(z, prepare_handler) >= 0);
assert_se(write(a[1], &ch, 1) >= 0);
assert_se(write(b[1], &ch, 1) >= 0);
assert_se(!got_a && !got_b && !got_c);
assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
assert_se(!got_a && got_b && !got_c);
assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
assert_se(!got_a && got_b && got_c);
assert_se(sd_event_run(e, (uint64_t) -1) >= 1);
assert_se(got_a && got_b && got_c);
sd_event_source_unref(x);
sd_event_source_unref(y);
do_quit = true;
assert_se(sd_event_source_set_time(z, now(CLOCK_MONOTONIC) + 200 * USEC_PER_MSEC) >= 0);
assert_se(sd_event_source_set_enabled(z, SD_EVENT_ONESHOT) >= 0);
assert_se(sd_event_loop(e) >= 0);
sd_event_source_unref(z);
sd_event_source_unref(q);
sd_event_source_unref(w);
sd_event_unref(e);
close_pipe(a);
close_pipe(b);
close_pipe(d);
close_pipe(k);
return 0;
}