On Mon, Oct 10, 2011 at 01:45:50PM +0200, Michal Privoznik wrote:
Currently, push & pop from event queue (both server & client
side)
rely on lock from higher levels, e.g. on driver lock (qemu),
private_data (remote), ...; This alone is not sufficient as not
every function that interacts with this queue can/does lock,
esp. in client where we have a different approach, "passing
the buck".
Therefore we need a separate lock just to protect event queue.
+static void
+virDomainEventStateLock(virDomainEventStatePtr state)
+{
+ if (!state)
+ return;
+
+ virMutexLock(&state->lock);
+}
+
+static void
+virDomainEventStateUnlock(virDomainEventStatePtr state)
+{
+ if (!state)
+ return;
+
+ virMutexUnlock(&state->lock);
+}
Normal coding policy is to *not* check for NULL in the mutex
functions. It is safer to crash on NULL, then to return without
locking.
virDomainEventStateQueue(virDomainEventStatePtr state,
virDomainEventPtr event)
{
+ int count;
+
if (state->timer < 0) {
virDomainEventFree(event);
return;
}
+ virDomainEventStateLock(state);
+
if (virDomainEventQueuePush(state->queue, event) < 0) {
VIR_DEBUG("Error adding event to queue");
virDomainEventFree(event);
}
- if (state->queue->count == 1)
+ count = state->queue->count;
+ virDomainEventStateUnlock(state);
+
+ if (count == 1)
virEventUpdateTimeout(state->timer, 0);
+
}
Updating the timer outside the lock is unsafe.
void
@@ -1182,6 +1217,7 @@ virDomainEventStateFlush(virDomainEventStatePtr state,
{
virDomainEventQueue tempQueue;
+ virDomainEventStateLock(state);
state->isDispatching = true;
/* Copy the queue, so we're reentrant safe when dispatchFunc drops the
@@ -1190,6 +1226,7 @@ virDomainEventStateFlush(virDomainEventStatePtr state,
tempQueue.events = state->queue->events;
state->queue->count = 0;
state->queue->events = NULL;
+ virDomainEventStateUnlock(state);
virEventUpdateTimeout(state->timer, -1);
virDomainEventQueueDispatch(&tempQueue,
This is unsafe
@@ -1198,9 +1235,11 @@
virDomainEventStateFlush(virDomainEventStatePtr state,
opaque);
/* Purge any deleted callbacks */
+ virDomainEventStateLock(state);
virDomainEventCallbackListPurgeMarked(state->callbacks);
state->isDispatching = false;
+ virDomainEventStateUnlock(state);
}
With these two timer updates you have a race condition with 2 threads:
T1: virDomainEventStateFlush T2: virDomainEventQueue
1. Lock
2. Set count = 0;
3. Unlock
4. lock
5. Save current count value
6. Queue new event
7. unlock
8. virEventUpdateTimer(1);
9. virEventUpdateTimer(-1)
Now the timer is disabled, but we have an event pending
You need to make sure all timer updates are *inside* the critical
section.
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 :|