Re: [libvirt] [PATCH 2/4] daemon: call free callbacks synchronously in event loop impl

2016-09-22 Thread Nikolay Shirokovskiy


On 22.09.2016 11:44, Daniel P. Berrange wrote:
> On Thu, Sep 22, 2016 at 11:38:50AM +0300, Nikolay Shirokovskiy wrote:
>> 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.
> 
> That is impossible to guarantee. The event loop impl is exposed
> via the public API, so arbitrary external applications get to
> call it.
> 
> So I don't think it is safe to make this change.
> 

Ok, the other I option I think of is to add call to indicate event loop
is finished so that there is no need to defer cleanups. 


Nikolay

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH 2/4] daemon: call free callbacks synchronously in event loop impl

2016-09-22 Thread Daniel P. Berrange
On Thu, Sep 22, 2016 at 11:38:50AM +0300, Nikolay Shirokovskiy wrote:
> 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.

That is impossible to guarantee. The event loop impl is exposed
via the public API, so arbitrary external applications get to
call it.

So I don't think it is safe to make this change.


Regards,
Daniel
-- 
|: http://berrange.com  -o-http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org  -o- http://virt-manager.org :|
|: http://autobuild.org   -o- http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org   -o-   http://live.gnome.org/gtk-vnc :|

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


[libvirt] [PATCH 2/4] daemon: call free callbacks synchronously in event loop impl

2016-09-22 Thread Nikolay Shirokovskiy
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.handles[i].ff(eventLoop.handles[i].opaque);
+virMutexLock();
+}
 virEventPollInterruptLocked();
 virMutexUnlock();
 return 0;
@@ -315,6 +320,11 @@ int