diff options
author | Lennart Poettering <lennart@poettering.net> | 2013-12-13 04:03:30 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2013-12-13 04:06:43 +0100 |
commit | 12179984a38fe74581333fbcdc11c822d81f505f (patch) | |
tree | 0fbd335d4c9f2d96df48a5e7a7f7f816c7eca347 /src/libsystemd-bus/sd-event.c | |
parent | 7b77ed8cf36e8eca6017791626044b61ae2d68e7 (diff) |
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.
Diffstat (limited to 'src/libsystemd-bus/sd-event.c')
-rw-r--r-- | src/libsystemd-bus/sd-event.c | 40 |
1 files changed, 33 insertions, 7 deletions
diff --git a/src/libsystemd-bus/sd-event.c b/src/libsystemd-bus/sd-event.c index b3964325a0..6af52ecb3c 100644 --- a/src/libsystemd-bus/sd-event.c +++ b/src/libsystemd-bus/sd-event.c @@ -58,6 +58,7 @@ struct sd_event_source { EventSourceType type:4; int enabled:3; bool pending:1; + bool dispatching:1; int priority; unsigned pending_index; @@ -993,8 +994,21 @@ _public_ sd_event_source* sd_event_source_unref(sd_event_source *s) { 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); + } return NULL; } @@ -1689,7 +1703,7 @@ static int source_dispatch(sd_event_source *s) { return r; } - sd_event_source_ref(s); + s->dispatching = true; switch (s->type) { @@ -1734,12 +1748,16 @@ static int source_dispatch(sd_event_source *s) { break; } - if (r < 0) { + s->dispatching = false; + + if (r < 0) 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_unref(s); return 1; } @@ -1761,10 +1779,18 @@ static int event_prepare(sd_event *e) { return r; assert(s->prepare); + + s->dispatching = true; r = s->prepare(s, s->userdata); + s->dispatching = false; + if (r < 0) - return r; + 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); } return 0; |