This is the culprit of what you’re observing. req
becomes a dangling pointer as soon as a request is completed. In most cases (which is why we typically need > 1 number of iterations), this just means that g_requests.erase(req)
removes 0 elements because it points to a non-existing request, but sometimes (and actually quite frequently as addresses seem to be reused quite aggressively) this will point to an unrelated request which has already been added into g_requests
, erasing it unintentionally.
The underlying issue is that an evhttp_connection
and an evhttp_request
simply have different lifecycles, with connections living longer than requests. It seems that passing req
as an argument to evhttp_connection_set_closecb
is a fundamentally unsafe thing to do if we can’t guarantee that req
hasn’t been completed by the time closecb
is called (which in the happy path, we always expect it to be).