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.
- fix sd-event hookup when we connect to multiple servers one after the other
* sd-event
- fix sd-event hookup when we connect to multiple servers one after the other
* sd-event
- - allow multiple signal handlers per signal
- - when dispatching an event source then _unref() on it should remove it from the epoll
+ - allow multiple signal handlers per signal?
* in the final killing spree, detect processes from the root directory, and
complain loudly if they have argv[0][0] == '@' set.
* in the final killing spree, detect processes from the root directory, and
complain loudly if they have argv[0][0] == '@' set.
EventSourceType type:4;
int enabled:3;
bool pending:1;
EventSourceType type:4;
int enabled:3;
bool pending:1;
int priority;
unsigned pending_index;
int priority;
unsigned pending_index;
assert(s->n_ref >= 1);
s->n_ref--;
assert(s->n_ref >= 1);
s->n_ref--;
- if (s->n_ref <= 0)
- source_free(s);
+ 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) {
+ if (s->type == SOURCE_IO)
+ source_io_unregister(s);
+ } else
+ source_free(s);
+ }
- sd_event_source_ref(s);
+ s->dispatching = false;
+
+ if (r < 0)
log_debug("Event source %p returned error, disabling: %s", s, strerror(-r));
log_debug("Event source %p returned error, disabling: %s", s, strerror(-r));
+
+ if (s->n_ref == 0)
+ source_free(s);
+ else if (r < 0)
sd_event_source_set_enabled(s, SD_EVENT_OFF);
sd_event_source_set_enabled(s, SD_EVENT_OFF);
- sd_event_source_unref(s);
return r;
assert(s->prepare);
return r;
assert(s->prepare);
+
+ s->dispatching = true;
r = s->prepare(s, s->userdata);
r = s->prepare(s, s->userdata);
+ s->dispatching = false;
+
+ log_debug("Prepare callback of event source %p returned error, disabling: %s", s, strerror(-r));
+ if (s->n_ref == 0)
+ source_free(s);
+ else if (r < 0)
+ sd_event_source_set_enabled(s, SD_EVENT_OFF);
-static bool got_a, got_b, got_c;
+static bool got_a, got_b, got_c, got_unref;
+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));
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));
int main(int argc, char *argv[]) {
sd_event *e = NULL;
int main(int argc, char *argv[]) {
sd_event *e = NULL;
- sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL;
+ sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
static const char ch = 'x';
static const char ch = 'x';
- int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1};
+ 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(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);
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
got_a = false, got_b = false, got_c = false, got_d = 0;
/* Add a oneshot handler, trigger it, re-enable it, and trigger
close_pipe(a);
close_pipe(b);
close_pipe(a);
close_pipe(b);
+ close_pipe(d);
+ close_pipe(k);