[libvirt] [PATCH] test: share state driver between test:///default connections

2014-01-01 Thread Eric Blake
Prior to this patch, every test:/// URI has its own event manager,
which means that registering for an event can only ever receive
events from the connection where it issued the API that triggered
the event.  But the whole idea of events is to be able to learn
about something where an API call did NOT trigger the action.

In order to actually test asynchronous events, I wanted to be able
to tie multiple test connections to the same state.  Use of a file
in a test URI is still per-connection state, but now parallel
connections to test:///default (from the same binary, of course)
now share common state and can affect one another.

Here's the test program I used to expose the difference (maybe not
the most polished, but does the trick):

 #include stdbool.h
 #include stdlib.h
 #include errno.h
 #include stdio.h
 #include unistd.h
 #include signal.h
 #include libvirt/libvirt.h
 #include libvirt/virterror.h

static int counter;
static int counter2;
static virConnectPtr conn1;
static virConnectPtr conn2;
static virDomainPtr dom1;
static virDomainPtr dom2;

static int
common(virConnectPtr conn, virDomainPtr dom, int event, int detail,
   void *opaque, int caller)
{
printf(%d: in callback %d, from domain %s (%d), event/detail %d/%d\n,
   counter2++, caller, virDomainGetName(dom),
   virDomainGetID(dom), event, detail);
return 0;
}

static int
callback1(virConnectPtr conn, virDomainPtr dom, int event, int detail,
  void *opaque)
{
return common(conn, dom, event, detail, opaque, 1);
}

static int
callback2(virConnectPtr conn, virDomainPtr dom, int event, int detail,
  void *opaque)
{
return common(conn, dom, event, detail, opaque, 2);
}

static bool quit;
static void
handler(int sig)
{
quit = true;
}
static void
timer(int id, void *opaque)
{
printf(%d: timer %d firing\n, counter++, id);
unsigned long l;
switch (counter) {
case 1:
if (virDomainSuspend(dom2)  0)
exit(10);
printf(suspended\n);
break;
case 2:
if (virDomainResume(dom2)  0)
exit(11);
printf(resumed\n);
break;
case 4:
quit = true;
/* fallthrough */
default:
virConnectGetLibVersion(conn1, l);
break;
}
}

int main(int argc, char **argv)
{
signal(SIGINT, handler);
conn1 = virConnectOpen(test:///default);
conn2 = virConnectOpen(test:///default);
if (!conn1 || !conn2)
return 1;
dom1 = virDomainLookupByName(conn1, test);
dom2 = virDomainLookupByName(conn2, test);
if (!dom1 || !dom2)
return 2;
if (virEventRegisterDefaultImpl()  0)
return 3;
int id0 = virEventAddTimeout(1000, timer, NULL, NULL);
if (id0  0)
return 4;
int id1 = virConnectDomainEventRegisterAny(conn1, dom1,
   VIR_DOMAIN_EVENT_ID_LIFECYCLE,
   
VIR_DOMAIN_EVENT_CALLBACK(callback1),
   NULL, NULL);
if (id1  0)
return 5;
int id2 = -1;
if (argc  1) {
id2 = virConnectDomainEventRegisterAny(conn2, dom2,
   VIR_DOMAIN_EVENT_ID_LIFECYCLE,
   
VIR_DOMAIN_EVENT_CALLBACK(callback2),
   NULL, NULL);
if (id2  0)
return 6;
if (argc  2) {
if (virConnectDomainEventDeregisterAny(conn2, id2)  0)
return 7;
}
}

while (!quit)
if (virEventRunDefaultImpl()  0)
return 20;
if (virConnectDomainEventDeregisterAny(conn1, id1)  0)
return 21;
if (id2 = 0  virConnectDomainEventDeregisterAny(conn2, id2)  0)
return 22;
if (virEventRemoveTimeout(id0)  0)
return 23;
if (virDomainFree(dom1)  0 || virDomainFree(dom2)  0 ||
virConnectClose(conn1)  0 || virConnectClose(conn2)  0)
return 24;
return 0;
}

Pre-patch results:
$ ./run ./bar
0: timer 1 firing
suspended
1: timer 1 firing
resumed
2: timer 1 firing
3: timer 1 firing

Post-patch results:

$ ./run ./bar
0: timer 1 firing
suspended
0: in callback 1, from domain test (1), event/detail 3/0
1: timer 1 firing
resumed
1: in callback 1, from domain test (1), event/detail 4/0
2: timer 1 firing
3: timer 1 firing
$ ./run ./bar 1
0: timer 1 firing
suspended
0: in callback 1, from domain test (1), event/detail 3/0
1: in callback 2, from domain test (1), event/detail 3/0
1: timer 1 firing
resumed
2: in callback 1, from domain test (1), event/detail 4/0
3: in callback 2, from domain test (1), event/detail 4/0
2: timer 1 firing
3: timer 1 firing

Valgrind didn't report any leaks.

* src/test/test_driver.c (testConnectOpen): Move per-connection
state initialization...
(testOpenFromFile): ...here.
(defaultConn, defaultConnections, defaultLock, testOnceInit): New
shared state.
(testOpenDefault): 

Re: [libvirt] [PATCH] test: share state driver between test:///default connections

2014-01-01 Thread Eric Blake
On 01/01/2014 09:54 AM, Eric Blake wrote:
 Prior to this patch, every test:/// URI has its own event manager,
 which means that registering for an event can only ever receive
 events from the connection where it issued the API that triggered
 the event.  But the whole idea of events is to be able to learn
 about something where an API call did NOT trigger the action.
 

 ---
 
 I'm actually playing with moving my test out of the commit message
 and into tests/objecteventtest.c; if I get that working, I'll
 post a v2.

Turned out to be easier than I feared.

https://www.redhat.com/archives/libvir-list/2014-January/msg00022.html

-- 
Eric Blake   eblake redhat com+1-919-301-3266
Libvirt virtualization library http://libvirt.org



signature.asc
Description: OpenPGP digital signature
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list