4

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?

dpassage
  • 5,423
  • 3
  • 25
  • 53
  • That sounds quite odd, `bufferevent_free` disables all events and cancels any pending events before the event is freed. Are there other things going on (e.g. is this multi threaded code ? and which libevent version are you using ?) – nos Jul 29 '15 at 00:06
  • It's multi-threaded code, but I've verified that these calls to libevent only happen from callbacks on the event loops's thread. I'll add some more detail to the question. – dpassage Jul 29 '15 at 17:03
  • Edited with more details. Also get the same behavior with libevent 2.1.5-beta – dpassage Jul 29 '15 at 23:06

1 Answers1

1

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);
sqr163
  • 1,074
  • 13
  • 24
  • I also had to call evtimer_del() but sadly still get the timeout callback after calling bufferevent_free(bev); and more importantly, after calling event_base_free(base); ... so I still get a SIGSEVF – Code Abominator Feb 27 '18 at 04:11
  • 1
    @CodeAbominator insteal evtimer_del() try event_del(). – sqr163 Feb 27 '18 at 04:30