I'm doing some work with libevent, version 2.0.22, and I'm struggling with dealing with bufferevent_free
and making sure sockets close at the right time. This is within an iOS app built with Xcode 6.4, running on iOS 8.4.
Each socket is managed by a struct bufferevent
, and I also have a data structure which keeps track of application state for that socket:
bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE);
bufferevent_socket_connect_hostname(bev, dns_base, AF_UNSPEC, host, port_number);
struct stream_data *data = malloc(sizeof(struct stream_data));
/* initialize stream data here */
bufferevent_setcb(bev, read_cb, write_cb, event_cb, data);
In a callback from some other buffer socket, I decide I don't need the buffer I just tried to open. This happens before I get the connected callback on the bev in question. Because I created it with BEV_OPT_CLOSE_ON_FREE
, I just free it. Then I delete the data structure I'm using. Looks like this:
bufferevent_free(bev);
free(stream_data); // the data corresponding to that bev
In this case, though, the socket actually finished connecting in the meantime. So my event callback fires:
void event_cb(struct bufferevent *bev, short what, void *ctx)
{
struct stream_data *data = ctx;
// data now points to already freed memory
}
And now I've got a pointer to already freed memory. I've confirmed with debugger breakpoints, NSLog
, etc that the event callback is firing after the free
above.
Is this expected behavior? If so, how can I ever tell that a bufferevent I freed is well and truly gone, making it safe to remove my own data structures?
Yes, this is expected libevent behavior: after bufferevent_free() it still may invoke your callbacks. From libevent book:
Bufferevents are internally reference-counted, so if the bufferevent has pending deferred callbacks when you free it, it won’t be deleted until the callbacks are done.
The easiest solutions is to remove all the callbacks before freeing the bufferevent object:
bufferevent_setcb(bev, NULL, NULL, NULL, NULL);
bufferevent_free(bev);