Default event loop impl delays deleting registered handles and timeouts so that deleting is safe from event handlers. But this means deletions after event loop is finished will never occur and particularly assosiated callback objects will not be freed. For this reason network clients that exist at the moment of libvirtd daemon exit are never get disposed and daemon object itself too if there are admin server clients. Let's call free callbacks immediately we don't need to delay this operation, only removing handles from the list. This breaks virnetsocket.c immediately as socket is locked in freeing callback and callback is called synchronously from virNetSocketRemoveIOCallback which holds the lock already. So fix it to pass intermediate callback object to the loop instead of socket itself. I've checked other users of virEventAddHandle, looks like they don't have such problems. --- src/rpc/virnetsocket.c | 70 ++++++++++++++++++++++--------------------------- src/util/vireventpoll.c | 24 +++++++---------- 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 405f5ba..732a993 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -69,6 +69,16 @@ VIR_LOG_INIT("rpc.netsocket"); +struct _virNetSocketCallbackObject { + virNetSocketPtr sock; + virNetSocketIOFunc func; + void *opaque; + virFreeCallback ff; +}; + +typedef struct _virNetSocketCallbackObject virNetSocketCallbackObject; +typedef virNetSocketCallbackObject *virNetSocketCallbackObjectPtr; + struct _virNetSocket { virObjectLockable parent; @@ -78,11 +88,6 @@ struct _virNetSocket { int errfd; bool client; - /* Event callback fields */ - virNetSocketIOFunc func; - void *opaque; - virFreeCallback ff; - virSocketAddr localAddr; virSocketAddr remoteAddr; char *localAddrStrSASL; @@ -1928,38 +1933,19 @@ static void virNetSocketEventHandle(int watch ATTRIBUTE_UNUSED, int events, void *opaque) { - virNetSocketPtr sock = opaque; - virNetSocketIOFunc func; - void *eopaque; - - virObjectLock(sock); - func = sock->func; - eopaque = sock->opaque; - virObjectUnlock(sock); - - if (func) - func(sock, events, eopaque); + virNetSocketCallbackObjectPtr o = opaque; + if (o->func) + o->func(o->sock, events, o->opaque); } static void virNetSocketEventFree(void *opaque) { - virNetSocketPtr sock = opaque; - virFreeCallback ff; - void *eopaque; - - virObjectLock(sock); - ff = sock->ff; - eopaque = sock->opaque; - sock->func = NULL; - sock->ff = NULL; - sock->opaque = NULL; - virObjectUnlock(sock); - - if (ff) - ff(eopaque); - - virObjectUnref(sock); + virNetSocketCallbackObjectPtr o = opaque; + if (o->ff) + o->ff(o->opaque); + virObjectUnref(o->sock); + VIR_FREE(o); } int virNetSocketAddIOCallback(virNetSocketPtr sock, @@ -1969,32 +1955,40 @@ int virNetSocketAddIOCallback(virNetSocketPtr sock, virFreeCallback ff) { int ret = -1; + virNetSocketCallbackObjectPtr cbobj = NULL; - virObjectRef(sock); virObjectLock(sock); if (sock->watch >= 0) { VIR_DEBUG("Watch already registered on socket %p", sock); goto cleanup; } + if (VIR_ALLOC(cbobj) < 0) + goto cleanup; + + cbobj->sock = virObjectRef(sock); + cbobj->func = func; + cbobj->opaque = opaque; + cbobj->ff = ff; + if ((sock->watch = virEventAddHandle(sock->fd, events, virNetSocketEventHandle, - sock, + cbobj, virNetSocketEventFree)) < 0) { VIR_DEBUG("Failed to register watch on socket %p", sock); goto cleanup; } - sock->func = func; - sock->opaque = opaque; - sock->ff = ff; ret = 0; cleanup: virObjectUnlock(sock); - if (ret != 0) + if (ret != 0) { + VIR_FREE(cbobj); virObjectUnref(sock); + } + return ret; } diff --git a/src/util/vireventpoll.c b/src/util/vireventpoll.c index 81ecab4..e152d23 100644 --- a/src/util/vireventpoll.c +++ b/src/util/vireventpoll.c @@ -198,6 +198,11 @@ int virEventPollRemoveHandle(int watch) if (eventLoop.handles[i].watch == watch) { EVENT_DEBUG("mark delete %zu %d", i, eventLoop.handles[i].fd); eventLoop.handles[i].deleted = 1; + if (eventLoop.handles[i].ff) { + virMutexUnlock(&eventLoop.lock); + eventLoop.handles[i].ff(eventLoop.handles[i].opaque); + virMutexLock(&eventLoop.lock); + } virEventPollInterruptLocked(); virMutexUnlock(&eventLoop.lock); return 0; @@ -315,6 +320,11 @@ int virEventPollRemoveTimeout(int timer) continue; if (eventLoop.timeouts[i].timer == timer) { + if (eventLoop.timeouts[i].ff) { + virMutexUnlock(&eventLoop.lock); + eventLoop.timeouts[i].ff(eventLoop.timeouts[i].opaque); + virMutexLock(&eventLoop.lock); + } eventLoop.timeouts[i].deleted = 1; virEventPollInterruptLocked(); virMutexUnlock(&eventLoop.lock); @@ -536,13 +546,6 @@ static void virEventPollCleanupTimeouts(void) PROBE(EVENT_POLL_PURGE_TIMEOUT, "timer=%d", eventLoop.timeouts[i].timer); - if (eventLoop.timeouts[i].ff) { - virFreeCallback ff = eventLoop.timeouts[i].ff; - void *opaque = eventLoop.timeouts[i].opaque; - virMutexUnlock(&eventLoop.lock); - ff(opaque); - virMutexLock(&eventLoop.lock); - } if ((i+1) < eventLoop.timeoutsCount) { memmove(eventLoop.timeouts+i, @@ -585,13 +588,6 @@ static void virEventPollCleanupHandles(void) PROBE(EVENT_POLL_PURGE_HANDLE, "watch=%d", eventLoop.handles[i].watch); - if (eventLoop.handles[i].ff) { - virFreeCallback ff = eventLoop.handles[i].ff; - void *opaque = eventLoop.handles[i].opaque; - virMutexUnlock(&eventLoop.lock); - ff(opaque); - virMutexLock(&eventLoop.lock); - } if ((i+1) < eventLoop.handlesCount) { memmove(eventLoop.handles+i, -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list