]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Fix deadlock in remote driver domain events
authorDaniel P. Berrange <berrange@redhat.com>
Tue, 28 Jul 2009 16:45:19 +0000 (17:45 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 29 Jul 2009 14:25:18 +0000 (15:25 +0100)
* src/remote_internal.c: Release driver lock when dispatching events
  to callbacks

src/remote_internal.c

index f20ed6ef3d95bc98e7aaf7c7b097afa4ef8495d8..a58b768585f3e70bf36886bd9d235afa64dcc3bb 100644 (file)
@@ -167,6 +167,8 @@ struct private_data {
     virDomainEventQueuePtr domainEvents;
     /* Timer for flushing domainEvents queue */
     int eventFlushTimer;
+    /* Flag if we're in process of dispatching */
+    int domainEventDispatching;
 
     /* Self-pipe to wakeup threads waiting in poll() */
     int wakeupSendFD;
@@ -6288,18 +6290,26 @@ static int remoteDomainEventDeregister (virConnectPtr conn,
 
     remoteDriverLock(priv);
 
-    if (virDomainEventCallbackListRemove(conn, priv->callbackList,
-                                         callback) < 0) {
-         error (conn, VIR_ERR_RPC, _("removing cb fron list"));
-         goto done;
-    }
-
-    if ( priv->callbackList->count == 0 ) {
-        /* Tell the server when we are the last callback deregistering */
-        if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER,
-                (xdrproc_t) xdr_void, (char *) NULL,
-                (xdrproc_t) xdr_void, (char *) NULL) == -1)
+    if (priv->domainEventDispatching) {
+        if (virDomainEventCallbackListMarkDelete(conn, priv->callbackList,
+                                                 callback) < 0) {
+            error (conn, VIR_ERR_RPC, _("marking cb for deletion"));
             goto done;
+        }
+    } else {
+        if (virDomainEventCallbackListRemove(conn, priv->callbackList,
+                                             callback) < 0) {
+            error (conn, VIR_ERR_RPC, _("removing cb from list"));
+            goto done;
+        }
+
+        if ( priv->callbackList->count == 0 ) {
+            /* Tell the server when we are the last callback deregistering */
+            if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER,
+                      (xdrproc_t) xdr_void, (char *) NULL,
+                      (xdrproc_t) xdr_void, (char *) NULL) == -1)
+                goto done;
+        }
     }
 
     rv = 0;
@@ -7309,18 +7319,54 @@ done:
     remoteDriverUnlock(priv);
 }
 
+static void remoteDomainEventDispatchFunc(virConnectPtr conn,
+                                          virDomainEventPtr event,
+                                          virConnectDomainEventCallback cb,
+                                          void *cbopaque,
+                                          void *opaque)
+{
+    struct private_data *priv = opaque;
+
+    /* Drop the lock whle dispatching, for sake of re-entrancy */
+    remoteDriverUnlock(priv);
+    virDomainEventDispatchDefaultFunc(conn, event, cb, cbopaque, NULL);
+    remoteDriverLock(priv);
+}
+
 void
 remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque)
 {
     virConnectPtr conn = opaque;
     struct private_data *priv = conn->privateData;
+    virDomainEventQueue tempQueue;
 
     remoteDriverLock(priv);
 
-    virDomainEventQueueDispatch(priv->domainEvents, priv->callbackList,
-                                virDomainEventDispatchDefaultFunc, NULL);
+    priv->domainEventDispatching = 1;
+
+    /* Copy the queue, so we're reentrant safe */
+    tempQueue.count = priv->domainEvents->count;
+    tempQueue.events = priv->domainEvents->events;
+    priv->domainEvents->count = 0;
+    priv->domainEvents->events = NULL;
+
+    virDomainEventQueueDispatch(&tempQueue, priv->callbackList,
+                                remoteDomainEventDispatchFunc, priv);
     virEventUpdateTimeout(priv->eventFlushTimer, -1);
 
+    /* Purge any deleted callbacks */
+    virDomainEventCallbackListPurgeMarked(priv->callbackList);
+
+    if ( priv->callbackList->count == 0 ) {
+        /* Tell the server when we are the last callback deregistering */
+        if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER,
+                  (xdrproc_t) xdr_void, (char *) NULL,
+                  (xdrproc_t) xdr_void, (char *) NULL) == -1)
+            VIR_WARN0("Failed to de-register events");
+    }
+
+    priv->domainEventDispatching = 0;
+
     remoteDriverUnlock(priv);
 }