On Sun, Nov 30, 2008 at 11:47:21PM +0000, Daniel P. Berrange wrote:
This patch adds thread safety to the domain events processing code
of
the QEMU driver. This entailed rather a large refactoring of the domain
events code and its quite complicated to explain.
- A convenient helper is added for creating event queues
virDomainEventQueuePtr virDomainEventQueueNew(void);
- Convenient helpers are added for creating virDomainEventPtr instances
from a variety of sources - each driver has different needs
virDomainEventPtr virDomainEventNew(int id, const char *name, const unsigned char
*uuid, int type, int detail);
virDomainEventPtr virDomainEventNewFromDom(virDomainPtr dom, int type, int detail);
virDomainEventPtr virDomainEventNewFromObj(virDomainObjPtr obj, int type, int
detail);
virDomainEventPtr virDomainEventNewFromDef(virDomainDefPtr def, int type, int
detail);
- The virDomainEvent struct held a reference to a virDomainPtr object.
This is replaced with a direct triple (id, name, uuid), avoiding the
need to instantiate virDomainPtr objects deep inside the QEMU code.
- The virDomainEventQueuePush() method is changed to take a
virDomainEventPtr object, rather than a virDomainPtr object
These last two changes are important to allow safe re-entrancy of the
event dispatch process. The virDomainEventPtr instance can be allocated
within a critical section lockde on the virDomainObjPtr instance, while
the event is queued outside the critical section, while only the driver
lock is held. Without this we'd have to hold the per-driver lock over
a much larger block of code which hurts concurrancy.
The QEMU driver cannot directly dispatch events, instead we have to
follow the remote driver and maintain a queue of pending events, and
use a timer to flush them. Again this is neccessary to improve
concurrency & provide safe re-entrancy.
Since we have two driver maintaining queues of evnts I add more
helper functions to allow code sharing
- Send a single vent to a list of callbacks:
void virDomainEventDispatch(virDomainEventPtr event,
virDomainEventCallbackListPtr cbs,
virDomainEventDispatchFunc dispatch,
void *opaque);
- Send a set of queued events to a list of callbacks
void virDomainEventQueueDispatch(virDomainEventQueuePtr queue,
virDomainEventCallbackListPtr cbs,
virDomainEventDispatchFunc dispatch,
void *opaque);
The virDomainEventDispatchFunc is what is invoked to finally dispatch
a single event, to a single registered callback. The default impl just
invokes the callback directly. The QEMU driver, however, uses a wrapper
which first releases its driver lock, invokes the callback, and then
re-aquires the lock.
As a further complication it is not safe for virDomainEventUnregister
to actually remove the callback from the list directly. Instead we
add a helper that simply marks it as removed, and then actually
purge it from a safe point in the code, when its guarenteed that the
driver isn't in the middle of dispatching.
As well as making the QEMU driver thread safe, this also takes the
opportunity to refactor the Xen / remote drivers to use more helpers
I must admit I'm a bit lost ...
@@ -192,14 +291,18 @@ virDomainEventQueueFree(virDomainEventQu
virDomainEventQueueFree(virDomainEventQueuePtr queue)
{
int i;
- for ( i=0 ; i<queue->count ; i++ ) {
- VIR_FREE(queue->events[i]);
+ if (!queue)
+ return;
+
+ for (i = 0; i < queue->count ; i++) {
+ virDomainEventFree(queue->events[i]);
}
+ VIR_FREE(queue->events);
VIR_FREE(queue);
}
okay we were leaking queue->events here !
[...]
struct _virDomainEventCallback {
virConnectPtr conn;
virConnectDomainEventCallback cb;
void *opaque;
virFreeCallback freecb;
-
+ int deleted;
};
Yup, I'm lost here ... seems we are making something asynchronous here
struct _virDomainEvent {
- virDomainPtr dom;
- int event;
+ int id;
+ char *name;
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ int type;
int detail;
};
Okay we don't want to held references anymore. But the lookups will
require search and locking, I completely fail to build a mental
representation where I can figure out why we don't risk deadlocks there.
There is a risk of lock reentrancy I'm sure but I can't see the model.
[...]
diff --git a/src/libvirt_sym.version.in b/src/libvirt_sym.version.in
--- a/src/libvirt_sym.version.in
+++ b/src/libvirt_sym.version.in
@@ -382,9 +382,21 @@ LIBVIRT_PRIVATE_@VERSION@ {
virDomainEventCallbackListFree;
virDomainEventCallbackListRemove;
virDomainEventCallbackListRemoveConn;
+ virDomainEventCallbackListMarkDelete;
+ virDomainEventCallbackListPurgeMarked;
+ virDomainEventQueueNew;
virDomainEventQueueFree;
- virDomainEventCallbackQueuePop;
- virDomainEventCallbackQueuePush;
+ virDomainEventQueuePop;
+ virDomainEventQueuePush;
+ virDomainEventNew;
+ virDomainEventNewFromDom;
+ virDomainEventNewFromObj;
+ virDomainEventNewFromDef;
+ virDomainEventFree;
+ virDomainEventDispatchDefaultFunc;
+ virDomainEventDispatch;
+ virDomainEventQueueDispatch;
+
reminds me I need to make room in libvirt_sym.version.in for APIs
introduced post 0.5.0, I have the (trivial) patch, will post it asap.
I'm lost, I failed to figure out most of the remaining of this patch,
but don't consider this a blocker, ...
Still I'm worried about the increased complexity of the code and
making harder for people to contribute patches or drivers.
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit
http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine
http://rpmfind.net/
http://veillard.com/ | virtualization library
http://libvirt.org/