[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
Re: [Libevent-users] Deadlock when calling bufferevent_free from an other thread
On 2012-08-08 2:52 PM, Nick Mathewson wrote:
On Mon, Aug 6, 2012 at 2:42 PM, Matthieu Nottale wrote:
I'm experiencing a deadlock on 2.0.19 while calling bufferevent_free frome
thread A, while thread B is in event_base_dispatch.
Ouch. This is a known bug. This fix is going to be hard. I wrote
about it here:
http://archives.seul.org/libevent/users/Feb-2012/msg00053.html
I've been thinking about this a little. As I mentioned quite some time
ago, I've seen what seems to be the same root problem (but of course
with a different manifestation) in a single-threaded application:
http://archives.seul.org/libevent/users/Jun-2012/msg00043.html Because
of that, I think this is fundamentally an ownership issue rather than a
threading issue. The problem is that the application is responsible for
deallocating events, bufferevents, etc. and their respective context
data, and the APIs for doing so are synchronous, but it cannot in
general know whether libevent holds live references to any of these
items. I don't think this can be fixed without application changes, but
I think I have a solution that involves only a pretty narrow change to
the API. (I'd like to make more drastic changes but those are not 2.1
material.)
To fix things, we need to change abstract behavior in two places.
First, when you event_del() an event while the event loop to which it
belongs is running, it does not actually get disassociated from the
event loop until the next time the loop is about to either block or exit
(this is called the "stabilization point"). It is not safe to
deallocate the event object before then. Second, we consider each event
object to _own_ its context data (if any), and therefore to be
responsible for deallocating it at the appropriate time, via an
application-provided callback (henceforth the "deallocation hook").
Concretely, when you call event_free, it _may_ synchronously deallocate
the event and call your deallocation hook to free the context data, but
if the event loop is running, it will just event_del the event and
return, and the actual deallocation will be queued to occur when the
event loop reaches the stabilization point. Similarly for
bufferevent_free and all other libevent objects that are wrapped around
an event.
For events allocated with event_new, if the application provides
nontrivial context data but does _not_ set a deallocation hook, we have
to allow the application to deallocate its context itself, when it
currently does (near-contemporaneous with event_free). I think the way
to handle that is, if the deallocation hook isn't set, we clear the
context pointer when event_free is called. This means that the
application may subsequently see an unexpected event callback with
context NULL, or be unable to retrieve its context point from the event
object, but that can only happen in circumstances where we would
previously have had a use-after-free condition, so we've converted a
dangerous bug into a less dangerous one.
For events allocated with event_assign, if you set a deallocation hook,
then it becomes safe to call event_free on that event. At the next
stabilization point, instead of the above behavior, the deallocation
hook will be called with the *event* as its argument; it is responsible
for deallocating everything at that time (event_get_callback_arg() is
guaranteed still to work).
Ideally you'd set the deallocation hook in event_new(), but we can't
easily add arguments to that function, so as the short-term narrow API
change I propose
typedef void (*event_dealloc_cb)(void *);
void event_set_dealloc(struct event *, event_dealloc_cb);
void bufferevent_set_dealloc(struct bufferevent *, event_dealloc_cb);
/* and so on for anything else that needs it */
What do you think? Most importantly, does this solve the problem?
Could it be polished?
zw
***********************************************************************
To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
unsubscribe libevent-users in the body.