The forthcoming set of patches will do alot of work relating to threads, which requires mutual exclusion primitives. This will include mutexes, condition variables and thread locals. We spefically do not need the ability to actually create thread though, at least not in the libvirt.so, only in the daemon. We currently stub out all the mutex calls to no-ops when pthread.h is missing. This is undesirable because it means we cannot clearly state the thread safety rules for use of libvirt. Saying it is thread safe on Linux, but not on Windows is IMHO not acceptable if we want to have portability of apps using libvirt. So this patch - Adds an internal API for mutexes, condition variables and thread locals - Implements this API for pthreads (covers Liunux, Solaris, and all BSD variants). - Implements this API for Win32 threads (covers Win32 :-) - Updates all code to use these internal APIs instead of pthread-XXX directly. - Adds missing mutex destroy calls from earlier patches. The pthreads implementation is utterly trivial, since I purposely designed the internal API to map 1-to-1 onto pthreads, albeit with many unneccessary bits not exposed. The Win32 implementatioin is more fun. Mutexes are trivial, I just followed the libxml2/glib impls for that. Thread locals are harder since Windows doesn't provide for any destructor to be called when a thread exits, meaning there's a potential memory leak. To address this I maintain some state for Win32 thread locals, in particular the destructor callback. I then added a DllMain() impl, which triggers upon thread-exit and explicitly calls the destructors. This is what DBus/win32-pthread does for thread locals. Finally condition variables are also hard because there's no native condition variable impl on Windows. I followed the GLib condition variable impl for this which uses a queue of waiters, a mutex and a Win32 event for wakeup. Not all that hard in the end. I did consider whether we could use win32-pthreads, but there's a few things I wasn't happy with. There are several different binary DLLs you have to choose between depending on what behaviour you want to C++ apps + exceptions. libvirt really shouldn't have to make that choice. More annoyingly, its pthread.h #include's its own config.h, so it seriously pollutes the global namespace with configure settings that clash with our own. This is a show stopper really. We also don't need the full ability to create threads, so wrapping Win32 thread natively wasn't very hard and that's what nearly every other app already does, so by using Win32 threads directly we should get better interoperabiltiy with other libraries doing the same. mingw32-libvirt.spec.in | 1 po/POTFILES.in | 1 proxy/Makefile.am | 1 qemud/qemud.c | 80 ++++++++++------ qemud/qemud.h | 7 - qemud/remote.c | 72 +++++++-------- src/Makefile.am | 7 + src/datatypes.c | 77 ++++++++-------- src/datatypes.h | 4 src/domain_conf.c | 26 ++--- src/domain_conf.h | 3 src/internal.h | 13 -- src/libvirt.c | 41 ++++++++ src/libvirt_private.syms | 12 ++ src/logging.c | 14 +- src/lxc_conf.h | 3 src/lxc_driver.c | 10 +- src/network_conf.c | 25 ++--- src/network_conf.h | 3 src/network_driver.c | 12 +- src/node_device.c | 4 src/node_device_conf.c | 27 ++--- src/node_device_conf.h | 5 - src/node_device_devkit.c | 8 + src/node_device_hal.c | 6 + src/openvz_conf.c | 13 ++ src/openvz_conf.h | 3 src/openvz_driver.c | 4 src/qemu_conf.h | 3 src/qemu_driver.c | 11 +- src/storage_conf.c | 26 ++--- src/storage_conf.h | 5 - src/storage_driver.c | 10 +- src/test.c | 35 ++++--- src/threads-pthread.c | 117 ++++++++++++++++++++++++ src/threads-pthread.h | 36 +++++++ src/threads-win32.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++ src/threads-win32.h | 39 ++++++++ src/threads.c | 34 +++++++ src/threads.h | 72 +++++++++++++++ src/uml_conf.h | 3 src/uml_driver.c | 10 +- src/virsh.c | 1 43 files changed, 871 insertions(+), 236 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -14,6 +14,7 @@ src/lxc_driver.c src/network_conf.c src/network_driver.c src/node_device.c +src/node_device_conf.c src/openvz_conf.c src/openvz_driver.c src/proxy_internal.c diff --git a/proxy/Makefile.am b/proxy/Makefile.am --- a/proxy/Makefile.am +++ b/proxy/Makefile.am @@ -12,6 +12,7 @@ libexec_PROGRAMS = libvirt_proxy libvirt_proxy_SOURCES = libvirt_proxy.c @top_srcdir@/src/xend_internal.c \ @top_srcdir@/src/xen_internal.c @top_srcdir@/src/virterror.c \ @top_srcdir@/src/sexpr.c \ + @top_srcdir@/src/threads.c \ @top_srcdir@/src/xs_internal.c @top_srcdir@/src/buf.c \ @top_srcdir@/src/capabilities.c \ @top_srcdir@/src/memory.c \ diff --git a/qemud/qemud.c b/qemud/qemud.c --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -268,11 +268,11 @@ qemudDispatchSignalEvent(int watch ATTRI siginfo_t siginfo; int ret; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); if (saferead(server->sigread, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) { VIR_ERROR(_("Failed to read from signal pipe: %s"), strerror(errno)); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); return; } @@ -300,7 +300,7 @@ qemudDispatchSignalEvent(int watch ATTRI if (ret != 0) server->shutdown = 1; - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); } int qemudSetCloseExec(int fd) { @@ -688,9 +688,14 @@ static struct qemud_server *qemudInitial return NULL; } - if (pthread_mutex_init(&server->lock, NULL) != 0) { + if (virMutexInit(&server->lock) < 0) { + VIR_ERROR("%s", _("cannot initialize mutex")); VIR_FREE(server); - return NULL; + } + if (virCondInit(&server->job) < 0) { + VIR_ERROR("%s", _("cannot initialize condition variable")); + virMutexDestroy(&server->lock); + VIR_FREE(server); } server->sigread = sigread; @@ -1117,8 +1122,11 @@ static int qemudDispatchServer(struct qe if (VIR_ALLOC(client) < 0) goto cleanup; - if (pthread_mutex_init(&client->lock, NULL) != 0) + if (virMutexInit(&client->lock) < 0) { + VIR_ERROR("%s", _("cannot initialize mutex")); + VIR_FREE(client); goto cleanup; + } client->magic = QEMUD_CLIENT_MAGIC; client->fd = fd; @@ -1233,12 +1241,12 @@ static struct qemud_client *qemudPending { int i; for (i = 0 ; i < server->nclients ; i++) { - pthread_mutex_lock(&server->clients[i]->lock); + virMutexLock(&server->clients[i]->lock); if (server->clients[i]->mode == QEMUD_MODE_WAIT_DISPATCH) { /* Delibrately don't unlock client - caller wants the lock */ return server->clients[i]; } - pthread_mutex_unlock(&server->clients[i]->lock); + virMutexUnlock(&server->clients[i]->lock); } return NULL; } @@ -1250,10 +1258,14 @@ static void *qemudWorker(void *data) while (1) { struct qemud_client *client; int len; - pthread_mutex_lock(&server->lock); - while ((client = qemudPendingJob(server)) == NULL) - pthread_cond_wait(&server->job, &server->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + while ((client = qemudPendingJob(server)) == NULL) { + if (virCondWait(&server->job, &server->lock) < 0) { + virMutexUnlock(&server->lock); + return NULL; + } + } + virMutexUnlock(&server->lock); /* We own a locked client now... */ client->mode = QEMUD_MODE_IN_DISPATCH; @@ -1271,8 +1283,8 @@ static void *qemudWorker(void *data) qemudDispatchClientFailure(server, client); client->refs--; - pthread_mutex_unlock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&client->lock); + virMutexUnlock(&server->lock); } } @@ -1444,7 +1456,7 @@ static void qemudDispatchClientRead(stru if (qemudRegisterClientEvent(server, client, 1) < 0) qemudDispatchClientFailure(server, client); - pthread_cond_signal(&server->job); + virCondSignal(&server->job); break; } @@ -1627,7 +1639,7 @@ qemudDispatchClientEvent(int watch, int struct qemud_client *client = NULL; int i; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); for (i = 0 ; i < server->nclients ; i++) { if (server->clients[i]->watch == watch) { @@ -1637,12 +1649,12 @@ qemudDispatchClientEvent(int watch, int } if (!client) { - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); return; } - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); if (client->fd != fd) return; @@ -1653,7 +1665,7 @@ qemudDispatchClientEvent(int watch, int qemudDispatchClientRead(server, client); else qemudDispatchClientFailure(server, client); - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); } static int qemudRegisterClientEvent(struct qemud_server *server, @@ -1703,7 +1715,7 @@ qemudDispatchServerEvent(int watch, int struct qemud_server *server = (struct qemud_server *)opaque; struct qemud_socket *sock; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); sock = server->sockets; @@ -1717,7 +1729,7 @@ qemudDispatchServerEvent(int watch, int if (sock && sock->fd == fd && events) qemudDispatchServer(server, sock); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); } @@ -1752,7 +1764,7 @@ static int qemudRunLoop(struct qemud_ser int timerid = -1; int ret = -1, i; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); server->nworkers = min_workers; if (VIR_ALLOC_N(server->workers, server->nworkers) < 0) { @@ -1783,21 +1795,22 @@ static int qemudRunLoop(struct qemud_ser DEBUG("Scheduling shutdown timer %d", timerid); } - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); if (qemudOneLoop() < 0) break; - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); reprocess: for (i = 0 ; i < server->nclients ; i++) { int inactive; - pthread_mutex_lock(&server->clients[i]->lock); + virMutexLock(&server->clients[i]->lock); inactive = server->clients[i]->fd == -1 && server->clients[i]->refs == 0; - pthread_mutex_unlock(&server->clients[i]->lock); + virMutexUnlock(&server->clients[i]->lock); if (inactive) { if (server->clients[i]->conn) virConnectClose(server->clients[i]->conn); + virMutexDestroy(&server->clients[i]->lock); VIR_FREE(server->clients[i]); server->nclients--; if (i < server->nclients) { @@ -1826,13 +1839,13 @@ static int qemudRunLoop(struct qemud_ser for (i = 0 ; i < server->nworkers ; i++) { pthread_t thread = server->workers[i]; - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); pthread_join(thread, NULL); - pthread_mutex_lock(&server->lock); + virMutexLock(&server->lock); } free(server->workers); - pthread_mutex_unlock(&server->lock); + virMutexUnlock(&server->lock); return ret; } @@ -1862,7 +1875,12 @@ static void qemudCleanup(struct qemud_se virStateCleanup(); - free(server); + if (virCondDestroy(&server->job) < 0) { + ; + } + virMutexDestroy(&server->lock); + + VIR_FREE(server); } /* Allocate an array of malloc'd strings from the config file, filename diff --git a/qemud/qemud.h b/qemud/qemud.h --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -46,6 +46,7 @@ #include <rpc/xdr.h> #include "remote_protocol.h" #include "logging.h" +#include "threads.h" #ifdef __GNUC__ #ifdef HAVE_ANSIDECL_H @@ -88,7 +89,7 @@ enum qemud_sock_type { /* Stores the per-client connection state */ struct qemud_client { - PTHREAD_MUTEX_T(lock); + virMutex lock; int magic; @@ -149,8 +150,8 @@ struct qemud_socket { /* Main server state */ struct qemud_server { - pthread_mutex_t lock; - pthread_cond_t job; + virMutex lock; + virCond job; int nworkers; pthread_t *workers; diff --git a/qemud/remote.c b/qemud/remote.c --- a/qemud/remote.c +++ b/qemud/remote.c @@ -301,7 +301,7 @@ remoteDispatchClientRequest (struct qemu /* Call function. */ conn = client->conn; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); /* * When the RPC handler is called: @@ -315,9 +315,9 @@ remoteDispatchClientRequest (struct qemu */ rv = (data->fn)(server, client, conn, &rerr, &args, &ret); - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); xdr_free (data->args_filter, (char*)&args); @@ -412,9 +412,9 @@ remoteDispatchOpen (struct qemud_server return -1; } - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); name = args->name ? *args->name : NULL; @@ -433,7 +433,7 @@ remoteDispatchOpen (struct qemud_server remoteDispatchConnError(rerr, NULL); rc = client->conn ? 0 : -1; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return rc; } @@ -450,13 +450,13 @@ remoteDispatchClose (struct qemud_server remote_error *rerr ATTRIBUTE_UNUSED, void *args ATTRIBUTE_UNUSED, void *ret ATTRIBUTE_UNUSED) { - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); client->closing = 1; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; } @@ -2472,11 +2472,11 @@ remoteDispatchAuthList (struct qemud_ser remoteDispatchOOMError(rerr); return -1; } - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); ret->types.types_val[0] = client->auth; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; } @@ -2535,9 +2535,9 @@ remoteDispatchAuthSaslInit (struct qemud socklen_t salen; char *localAddr, *remoteAddr; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); REMOTE_DEBUG("Initialize SASL auth %d", client->fd); if (client->auth != REMOTE_AUTH_SASL || @@ -2663,13 +2663,13 @@ remoteDispatchAuthSaslInit (struct qemud goto authfail; } - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); error: - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } @@ -2787,9 +2787,9 @@ remoteDispatchAuthSaslStart (struct qemu unsigned int serveroutlen; int err; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); REMOTE_DEBUG("Start SASL auth %d", client->fd); if (client->auth != REMOTE_AUTH_SASL || @@ -2851,13 +2851,13 @@ remoteDispatchAuthSaslStart (struct qemu client->auth = REMOTE_AUTH_NONE; } - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); error: - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } @@ -2874,9 +2874,9 @@ remoteDispatchAuthSaslStep (struct qemud unsigned int serveroutlen; int err; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); REMOTE_DEBUG("Step SASL auth %d", client->fd); if (client->auth != REMOTE_AUTH_SASL || @@ -2939,13 +2939,13 @@ remoteDispatchAuthSaslStep (struct qemud client->auth = REMOTE_AUTH_NONE; } - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); error: - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } @@ -3011,9 +3011,9 @@ remoteDispatchAuthPolkit (struct qemud_s DBusError err; const char *action; - pthread_mutex_lock(&server->lock); - pthread_mutex_lock(&client->lock); - pthread_mutex_unlock(&server->lock); + virMutexLock(&server->lock); + virMutexLock(&client->lock); + virMutexUnlock(&server->lock); action = client->readonly ? "org.libvirt.unix.monitor" : @@ -3091,12 +3091,12 @@ remoteDispatchAuthPolkit (struct qemud_s ret->complete = 1; client->auth = REMOTE_AUTH_NONE; - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return 0; authfail: remoteDispatchAuthError(rerr); - pthread_mutex_unlock(&client->lock); + virMutexUnlock(&client->lock); return -1; } diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -46,14 +46,19 @@ UTIL_SOURCES = \ event.c event.h \ hash.c hash.h \ iptables.c iptables.h \ + logging.c logging.h \ memory.c memory.h \ qparams.c qparams.h \ + threads.c threads.h \ + threads-pthread.h \ + threads-win32.h \ uuid.c uuid.h \ util.c util.h \ virterror.c virterror_internal.h \ - logging.c logging.h \ xml.c xml.h +EXTRA_DIST += threads-pthread.c threads-win32.c + # Internal generic driver infrastructure DRIVER_SOURCES = \ driver.c driver.h \ diff --git a/src/datatypes.c b/src/datatypes.c --- a/src/datatypes.c +++ b/src/datatypes.c @@ -123,6 +123,11 @@ virGetConnect(void) { virLibConnError(NULL, VIR_ERR_NO_MEMORY, _("allocating connection")); goto failed; } + if (virMutexInit(&ret->lock) < 0) { + VIR_FREE(ret); + goto failed; + } + ret->magic = VIR_CONNECT_MAGIC; ret->driver = NULL; ret->networkDriver = NULL; @@ -144,8 +149,6 @@ virGetConnect(void) { if (ret->nodeDevices == NULL) goto failed; - pthread_mutex_init(&ret->lock, NULL); - ret->refs = 1; return(ret); @@ -162,7 +165,7 @@ failed: if (ret->nodeDevices != NULL) virHashFree(ret->nodeDevices, (virHashDeallocator) virNodeDeviceFree); - pthread_mutex_destroy(&ret->lock); + virMutexDestroy(&ret->lock); VIR_FREE(ret); } return(NULL); @@ -197,8 +200,8 @@ virReleaseConnect(virConnectPtr conn) { xmlFreeURI(conn->uri); - pthread_mutex_unlock(&conn->lock); - pthread_mutex_destroy(&conn->lock); + virMutexUnlock(&conn->lock); + virMutexDestroy(&conn->lock); VIR_FREE(conn); } @@ -219,7 +222,7 @@ virUnrefConnect(virConnectPtr conn) { virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); DEBUG("unref connection %p %d", conn, conn->refs); conn->refs--; refs = conn->refs; @@ -228,7 +231,7 @@ virUnrefConnect(virConnectPtr conn) { /* Already unlocked mutex */ return (0); } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return (refs); } @@ -253,7 +256,7 @@ virGetDomain(virConnectPtr conn, const c virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); /* TODO search by UUID first as they are better differenciators */ @@ -286,11 +289,11 @@ virGetDomain(virConnectPtr conn, const c DEBUG("Existing hash entry %p: refs now %d", ret, ret->refs+1); } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -337,7 +340,7 @@ virReleaseDomain(virDomainPtr domain) { return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -358,7 +361,7 @@ virUnrefDomain(virDomainPtr domain) { virLibConnError(domain->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&domain->conn->lock); + virMutexLock(&domain->conn->lock); DEBUG("unref domain %p %s %d", domain, domain->name, domain->refs); domain->refs--; refs = domain->refs; @@ -368,7 +371,7 @@ virUnrefDomain(virDomainPtr domain) { return (0); } - pthread_mutex_unlock(&domain->conn->lock); + virMutexUnlock(&domain->conn->lock); return (refs); } @@ -393,7 +396,7 @@ virGetNetwork(virConnectPtr conn, const virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); /* TODO search by UUID first as they are better differenciators */ @@ -422,11 +425,11 @@ virGetNetwork(virConnectPtr conn, const conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -473,7 +476,7 @@ virReleaseNetwork(virNetworkPtr network) return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -494,7 +497,7 @@ virUnrefNetwork(virNetworkPtr network) { virLibConnError(network->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&network->conn->lock); + virMutexLock(&network->conn->lock); DEBUG("unref network %p %s %d", network, network->name, network->refs); network->refs--; refs = network->refs; @@ -504,7 +507,7 @@ virUnrefNetwork(virNetworkPtr network) { return (0); } - pthread_mutex_unlock(&network->conn->lock); + virMutexUnlock(&network->conn->lock); return (refs); } @@ -530,7 +533,7 @@ virGetStoragePool(virConnectPtr conn, co virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); /* TODO search by UUID first as they are better differenciators */ @@ -559,11 +562,11 @@ virGetStoragePool(virConnectPtr conn, co conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -606,7 +609,7 @@ virReleaseStoragePool(virStoragePoolPtr return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -627,7 +630,7 @@ virUnrefStoragePool(virStoragePoolPtr po virLibConnError(pool->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&pool->conn->lock); + virMutexLock(&pool->conn->lock); DEBUG("unref pool %p %s %d", pool, pool->name, pool->refs); pool->refs--; refs = pool->refs; @@ -637,7 +640,7 @@ virUnrefStoragePool(virStoragePoolPtr po return (0); } - pthread_mutex_unlock(&pool->conn->lock); + virMutexUnlock(&pool->conn->lock); return (refs); } @@ -664,7 +667,7 @@ virGetStorageVol(virConnectPtr conn, con virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); ret = (virStorageVolPtr) virHashLookup(conn->storageVols, key); if (ret == NULL) { @@ -695,11 +698,11 @@ virGetStorageVol(virConnectPtr conn, con conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret->pool); @@ -744,7 +747,7 @@ virReleaseStorageVol(virStorageVolPtr vo return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -765,7 +768,7 @@ virUnrefStorageVol(virStorageVolPtr vol) virLibConnError(vol->conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(-1); } - pthread_mutex_lock(&vol->conn->lock); + virMutexLock(&vol->conn->lock); DEBUG("unref vol %p %s %d", vol, vol->name, vol->refs); vol->refs--; refs = vol->refs; @@ -775,7 +778,7 @@ virUnrefStorageVol(virStorageVolPtr vol) return (0); } - pthread_mutex_unlock(&vol->conn->lock); + virMutexUnlock(&vol->conn->lock); return (refs); } @@ -801,7 +804,7 @@ virGetNodeDevice(virConnectPtr conn, con virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); return(NULL); } - pthread_mutex_lock(&conn->lock); + virMutexLock(&conn->lock); ret = (virNodeDevicePtr) virHashLookup(conn->nodeDevices, name); if (ret == NULL) { @@ -825,11 +828,11 @@ virGetNodeDevice(virConnectPtr conn, con conn->refs++; } ret->refs++; - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); return(ret); error: - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); if (ret != NULL) { VIR_FREE(ret->name); VIR_FREE(ret); @@ -872,7 +875,7 @@ virReleaseNodeDevice(virNodeDevicePtr de return; } - pthread_mutex_unlock(&conn->lock); + virMutexUnlock(&conn->lock); } @@ -889,7 +892,7 @@ int virUnrefNodeDevice(virNodeDevicePtr dev) { int refs; - pthread_mutex_lock(&dev->conn->lock); + virMutexLock(&dev->conn->lock); DEBUG("unref dev %p %s %d", dev, dev->name, dev->refs); dev->refs--; refs = dev->refs; @@ -899,6 +902,6 @@ virUnrefNodeDevice(virNodeDevicePtr dev) return (0); } - pthread_mutex_unlock(&dev->conn->lock); + virMutexUnlock(&dev->conn->lock); return (refs); } diff --git a/src/datatypes.h b/src/datatypes.h --- a/src/datatypes.h +++ b/src/datatypes.h @@ -26,7 +26,7 @@ #include "hash.h" #include "driver.h" - +#include "threads.h" /** * VIR_CONNECT_MAGIC: @@ -125,7 +125,7 @@ struct _virConnect { * count of any virDomain/virNetwork object associated with * this connection */ - PTHREAD_MUTEX_T (lock); + virMutex lock; virHashTablePtr domains; /* hash table for known domains */ virHashTablePtr networks; /* hash table for known domains */ virHashTablePtr storagePools;/* hash table for known storage pools */ diff --git a/src/domain_conf.c b/src/domain_conf.c --- a/src/domain_conf.c +++ b/src/domain_conf.c @@ -433,6 +433,8 @@ void virDomainObjFree(virDomainObjPtr do VIR_FREE(dom->monitorpath); VIR_FREE(dom->vcpupids); + virMutexDestroy(&dom->lock); + VIR_FREE(dom); } @@ -471,7 +473,13 @@ virDomainObjPtr virDomainAssignDef(virCo return NULL; } - pthread_mutex_init(&domain->lock, NULL); + if (virMutexInit(&domain->lock) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(domain); + return NULL; + } + virDomainObjLock(domain); domain->state = VIR_DOMAIN_SHUTOFF; domain->def = def; @@ -3660,26 +3668,14 @@ const char *virDomainDefDefaultEmulator( } -#ifdef HAVE_PTHREAD_H - void virDomainObjLock(virDomainObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virDomainObjUnlock(virDomainObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else -void virDomainObjLock(virDomainObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virDomainObjUnlock(virDomainObjPtr obj ATTRIBUTE_UNUSED) -{ -} -#endif - #endif /* ! PROXY */ diff --git a/src/domain_conf.h b/src/domain_conf.h --- a/src/domain_conf.h +++ b/src/domain_conf.h @@ -31,6 +31,7 @@ #include "internal.h" #include "capabilities.h" #include "util.h" +#include "threads.h" /* Different types of hypervisor */ /* NB: Keep in sync with virDomainVirtTypeToString impl */ @@ -456,7 +457,7 @@ struct _virDomainDef { typedef struct _virDomainObj virDomainObj; typedef virDomainObj *virDomainObjPtr; struct _virDomainObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; int stdin_fd; int stdout_fd; diff --git a/src/internal.h b/src/internal.h --- a/src/internal.h +++ b/src/internal.h @@ -12,19 +12,6 @@ #include <sys/syslimits.h> #endif -#ifdef HAVE_PTHREAD_H -#include <pthread.h> -#define PTHREAD_MUTEX_T(v) pthread_mutex_t v -#else -/* Mutex functions disappear if we don't have pthread. */ -#define PTHREAD_MUTEX_T(v) /*empty*/ -#define pthread_mutex_init(lk,p) /*empty*/ -#define pthread_mutex_destroy(lk) /*empty*/ -#define pthread_mutex_lock(lk) /*empty*/ -#define pthread_mutex_unlock(lk) /*empty*/ -#define pthread_sigmask(h, s, o) sigprocmask((h), (s), (o)) -#endif - /* The library itself is allowed to use deprecated functions / * variables, so effectively undefine the deprecated attribute * which would otherwise be defined in libvirt.h. diff --git a/src/libvirt.c b/src/libvirt.c --- a/src/libvirt.c +++ b/src/libvirt.c @@ -253,8 +253,12 @@ virInitialize(void) #endif if (initialized) return(0); + initialized = 1; + if (virThreadInitialize() < 0) + return -1; + #ifdef ENABLE_DEBUG debugEnv = getenv("LIBVIRT_DEBUG"); if (debugEnv && *debugEnv && *debugEnv != '0') { @@ -316,6 +320,43 @@ virInitialize(void) return(0); } +#ifdef WIN32 +BOOL WINAPI +DllMain (HINSTANCE instance, DWORD reason, LPVOID ignore); + +BOOL WINAPI +DllMain (HINSTANCE instance ATTRIBUTE_UNUSED, + DWORD reason, + LPVOID ignore ATTRIBUTE_UNUSED) +{ + switch (reason) { + case DLL_PROCESS_ATTACH: + fprintf(stderr, "Initializing DLL\n"); + virInitialize(); + break; + + case DLL_THREAD_ATTACH: + fprintf(stderr, "Thread start\n"); + /* Nothing todo in libvirt yet */ + break; + + case DLL_THREAD_DETACH: + fprintf(stderr, "Thread exit\n"); + /* Release per-thread local data */ + virThreadOnExit(); + break; + + case DLL_PROCESS_DETACH: + fprintf(stderr, "Process exit\n"); + /* Don't bother releasing per-thread data + since (hopefully) windows cleans up + everything on process exit */ + break; + } + + return TRUE; +} +#endif /** diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -261,6 +261,18 @@ virStoragePoolObjLock; virStoragePoolObjUnlock; +# threads.h +virMutexInit; +virMutexDestroy; +virMutexLock; +virMutexUnlock; + +virCondInit; +virCondDestroy; +virCondWait; +virCondSignal; +virCondBroadcast; + # util.h virFileReadAll; virStrToLong_i; diff --git a/src/logging.c b/src/logging.c --- a/src/logging.c +++ b/src/logging.c @@ -37,6 +37,7 @@ #include "logging.h" #include "memory.h" #include "util.h" +#include "threads.h" #ifdef ENABLE_DEBUG int debugFlag = 0; @@ -129,15 +130,15 @@ static int virLogResetOutputs(void); /* * Logs accesses must be serialized though a mutex */ -PTHREAD_MUTEX_T(virLogMutex); +virMutex virLogMutex; static void virLogLock(void) { - pthread_mutex_lock(&virLogMutex); + virMutexLock(&virLogMutex); } static void virLogUnlock(void) { - pthread_mutex_unlock(&virLogMutex); + virMutexUnlock(&virLogMutex); } @@ -167,8 +168,11 @@ static int virLogInitialized = 0; int virLogStartup(void) { if (virLogInitialized) return(-1); + + if (virMutexInit(&virLogMutex) < 0) + return -1; + virLogInitialized = 1; - pthread_mutex_init(&virLogMutex, NULL); virLogLock(); virLogLen = 0; virLogStart = 0; @@ -214,7 +218,7 @@ void virLogShutdown(void) { virLogStart = 0; virLogEnd = 0; virLogUnlock(); - pthread_mutex_destroy(&virLogMutex); + virMutexDestroy(&virLogMutex); virLogInitialized = 0; } diff --git a/src/lxc_conf.h b/src/lxc_conf.h --- a/src/lxc_conf.h +++ b/src/lxc_conf.h @@ -29,6 +29,7 @@ #include "internal.h" #include "domain_conf.h" #include "capabilities.h" +#include "threads.h" #define LXC_CONFIG_DIR SYSCONF_DIR "/libvirt/lxc" #define LXC_STATE_DIR LOCAL_STATE_DIR "/run/libvirt/lxc" @@ -36,7 +37,7 @@ typedef struct __lxc_driver lxc_driver_t; struct __lxc_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; virCapsPtr caps; diff --git a/src/lxc_driver.c b/src/lxc_driver.c --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -57,11 +57,11 @@ static lxc_driver_t *lxc_driver = NULL; static void lxcDriverLock(lxc_driver_t *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void lxcDriverUnlock(lxc_driver_t *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } @@ -1135,7 +1135,10 @@ static int lxcStartup(void) if (VIR_ALLOC(lxc_driver) < 0) { return -1; } - pthread_mutex_init(&lxc_driver->lock, NULL); + if (virMutexInit(&lxc_driver->lock) < 0) { + VIR_FREE(lxc_driver); + return -1; + } lxcDriverLock(lxc_driver); /* Check that this is a container enabled kernel */ @@ -1228,6 +1231,7 @@ static int lxcShutdown(void) VIR_FREE(lxc_driver->stateDir); VIR_FREE(lxc_driver->logDir); lxcDriverUnlock(lxc_driver); + virMutexDestroy(&lxc_driver->lock); VIR_FREE(lxc_driver); lxc_driver = NULL; diff --git a/src/network_conf.c b/src/network_conf.c --- a/src/network_conf.c +++ b/src/network_conf.c @@ -126,6 +126,8 @@ void virNetworkObjFree(virNetworkObjPtr VIR_FREE(net->configFile); VIR_FREE(net->autostartLink); + virMutexDestroy(&net->lock); + VIR_FREE(net); } @@ -163,7 +165,12 @@ virNetworkObjPtr virNetworkAssignDef(vir virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL); return NULL; } - pthread_mutex_init(&network->lock, NULL); + if (virMutexInit(&network->lock) < 0) { + virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(network); + return NULL; + } virNetworkObjLock(network); network->def = def; @@ -823,25 +830,13 @@ int virNetworkDeleteConfig(virConnectPtr return 0; } -#ifdef HAVE_PTHREAD_H - void virNetworkObjLock(virNetworkObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virNetworkObjUnlock(virNetworkObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else -void virNetworkObjLock(virNetworkObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virNetworkObjUnlock(virNetworkObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -#endif diff --git a/src/network_conf.h b/src/network_conf.h --- a/src/network_conf.h +++ b/src/network_conf.h @@ -29,6 +29,7 @@ #include <libxml/xpath.h> #include "internal.h" +#include "threads.h" /* 2 possible types of forwarding */ enum virNetworkForwardType { @@ -82,7 +83,7 @@ struct _virNetworkDef { typedef struct _virNetworkObj virNetworkObj; typedef virNetworkObj *virNetworkObjPtr; struct _virNetworkObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; pid_t dnsmasqPid; unsigned int active : 1; diff --git a/src/network_driver.c b/src/network_driver.c --- a/src/network_driver.c +++ b/src/network_driver.c @@ -59,7 +59,7 @@ /* Main driver state */ struct network_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; virNetworkObjList networks; @@ -73,11 +73,11 @@ struct network_driver { static void networkDriverLock(struct network_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void networkDriverUnlock(struct network_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static int networkShutdown(void); @@ -134,7 +134,10 @@ networkStartup(void) { if (VIR_ALLOC(driverState) < 0) goto error; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + goto error; + } networkDriverLock(driverState); if (!uid) { @@ -290,6 +293,7 @@ networkShutdown(void) { iptablesContextFree(driverState->iptables); networkDriverUnlock(driverState); + virMutexDestroy(&driverState->lock); VIR_FREE(driverState); diff --git a/src/node_device.c b/src/node_device.c --- a/src/node_device.c +++ b/src/node_device.c @@ -48,12 +48,12 @@ static int dev_has_cap(const virNodeDevi void nodeDeviceLock(virDeviceMonitorStatePtr driver) { DEBUG("LOCK node %p", driver); - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } void nodeDeviceUnlock(virDeviceMonitorStatePtr driver) { DEBUG("UNLOCK node %p", driver); - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static int nodeNumOfDevices(virConnectPtr conn, diff --git a/src/node_device_conf.c b/src/node_device_conf.c --- a/src/node_device_conf.c +++ b/src/node_device_conf.c @@ -99,6 +99,8 @@ void virNodeDeviceObjFree(virNodeDeviceO if (dev->privateFree) (*dev->privateFree)(dev->privateData); + virMutexDestroy(&dev->lock); + VIR_FREE(dev); } @@ -128,12 +130,18 @@ virNodeDeviceObjPtr virNodeDeviceAssignD return NULL; } - pthread_mutex_init(&device->lock, NULL); + if (virMutexInit(&device->lock) < 0) { + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(device); + return NULL; + } virNodeDeviceObjLock(device); device->def = def; if (VIR_REALLOC_N(devs->objs, devs->count+1) < 0) { device->def = NULL; + virNodeDeviceObjUnlock(device); virNodeDeviceObjFree(device); virNodeDeviceReportError(conn, VIR_ERR_NO_MEMORY, NULL); return NULL; @@ -408,26 +416,13 @@ void virNodeDevCapsDefFree(virNodeDevCap } -#ifdef HAVE_PTHREAD_H - void virNodeDeviceObjLock(virNodeDeviceObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else - -void virNodeDeviceObjLock(virNodeDeviceObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -#endif diff --git a/src/node_device_conf.h b/src/node_device_conf.h --- a/src/node_device_conf.h +++ b/src/node_device_conf.h @@ -26,6 +26,7 @@ #include "internal.h" #include "util.h" +#include "threads.h" enum virNodeDevCapType { /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */ @@ -142,7 +143,7 @@ struct _virNodeDeviceDef { typedef struct _virNodeDeviceObj virNodeDeviceObj; typedef virNodeDeviceObj *virNodeDeviceObjPtr; struct _virNodeDeviceObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; virNodeDeviceDefPtr def; /* device definition */ void *privateData; /* driver-specific private data */ @@ -160,7 +161,7 @@ struct _virNodeDeviceObjList { typedef struct _virDeviceMonitorState virDeviceMonitorState; typedef virDeviceMonitorState *virDeviceMonitorStatePtr; struct _virDeviceMonitorState { - PTHREAD_MUTEX_T(lock); + virMutex lock; virNodeDeviceObjList devs; /* currently-known devices */ void *privateData; /* driver-specific private data */ diff --git a/src/node_device_devkit.c b/src/node_device_devkit.c --- a/src/node_device_devkit.c +++ b/src/node_device_devkit.c @@ -298,7 +298,10 @@ static int devkitDeviceMonitorStartup(vo if (VIR_ALLOC(driverState) < 0) return -1; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + return -1; + } g_type_init(); @@ -375,7 +378,8 @@ static int devkitDeviceMonitorShutdown(v virNodeDeviceObjListFree(&driverState->devs); if (devkit_client) g_object_unref(devkit_client); - nodeDeviceLock(driverState); + nodeDeviceUnlock(driverState); + virMutexDestroy(&driveState->lock); VIR_FREE(driverState); return 0; } diff --git a/src/node_device_hal.c b/src/node_device_hal.c --- a/src/node_device_hal.c +++ b/src/node_device_hal.c @@ -678,7 +678,10 @@ static int halDeviceMonitorStartup(void) if (VIR_ALLOC(driverState) < 0) return -1; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + return -1; + } nodeDeviceLock(driverState); /* Allocate and initialize a new HAL context */ @@ -770,6 +773,7 @@ static int halDeviceMonitorShutdown(void (void)libhal_ctx_shutdown(hal_ctx, NULL); (void)libhal_ctx_free(hal_ctx); nodeDeviceUnlock(driverState); + virMutexDestroy(&driverState->lock); VIR_FREE(driverState); return 0; } diff --git a/src/openvz_conf.c b/src/openvz_conf.c --- a/src/openvz_conf.c +++ b/src/openvz_conf.c @@ -392,11 +392,18 @@ int openvzLoadDomains(struct openvz_driv goto cleanup; } - if (VIR_ALLOC(dom) < 0 || - VIR_ALLOC(dom->def) < 0) + if (VIR_ALLOC(dom) < 0) goto no_memory; - pthread_mutex_init(&dom->lock, NULL); + if (virMutexInit(&dom->lock) < 0) { + openvzError(NULL, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(dom); + goto cleanup; + } + + if (VIR_ALLOC(dom->def) < 0) + goto no_memory; if (STREQ(status, "stopped")) dom->state = VIR_DOMAIN_SHUTOFF; diff --git a/src/openvz_conf.h b/src/openvz_conf.h --- a/src/openvz_conf.h +++ b/src/openvz_conf.h @@ -30,6 +30,7 @@ #include "internal.h" #include "domain_conf.h" +#include "threads.h" enum { OPENVZ_WARN, OPENVZ_ERR }; @@ -53,7 +54,7 @@ enum { OPENVZ_WARN, OPENVZ_ERR }; #define VZCTL_BRIDGE_MIN_VERSION ((3 * 1000 * 1000) + (0 * 1000) + 22 + 1) struct openvz_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; virCapsPtr caps; virDomainObjList domains; diff --git a/src/openvz_driver.c b/src/openvz_driver.c --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -70,12 +70,12 @@ static int openvzDomainSetVcpusInternal( static void openvzDriverLock(struct openvz_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void openvzDriverUnlock(struct openvz_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } struct openvz_driver ovz_driver; diff --git a/src/qemu_conf.h b/src/qemu_conf.h --- a/src/qemu_conf.h +++ b/src/qemu_conf.h @@ -32,6 +32,7 @@ #include "network_conf.h" #include "domain_conf.h" #include "domain_event.h" +#include "threads.h" #define qemudDebug(fmt, ...) do {} while(0) @@ -51,7 +52,7 @@ enum qemud_cmd_flags { /* Main driver state */ struct qemud_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; unsigned int qemuVersion; int nextvmid; diff --git a/src/qemu_driver.c b/src/qemu_driver.c --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -78,11 +78,11 @@ static int qemudShutdown(void); static void qemuDriverLock(struct qemud_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void qemuDriverUnlock(struct qemud_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static int qemudSetCloseExec(int fd) { @@ -273,7 +273,11 @@ qemudStartup(void) { if (VIR_ALLOC(qemu_driver) < 0) return -1; - pthread_mutex_init(&qemu_driver->lock, NULL); + if (virMutexInit(&qemu_driver->lock) < 0) { + qemudLog(QEMUD_ERROR, "%s", _("cannot initialize mutex")); + VIR_FREE(qemu_driver); + return -1; + } qemuDriverLock(qemu_driver); /* Don't have a dom0 so start from 1 */ @@ -482,6 +486,7 @@ qemudShutdown(void) { brShutdown(qemu_driver->brctl); qemuDriverUnlock(qemu_driver); + virMutexDestroy(&qemu_driver->lock); VIR_FREE(qemu_driver); return 0; diff --git a/src/storage_conf.c b/src/storage_conf.c --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -297,6 +297,9 @@ virStoragePoolObjFree(virStoragePoolObjP VIR_FREE(obj->configFile); VIR_FREE(obj->autostartLink); + + virMutexDestroy(&obj->lock); + VIR_FREE(obj); } @@ -1259,13 +1262,19 @@ virStoragePoolObjAssignDef(virConnectPtr return NULL; } - pthread_mutex_init(&pool->lock, NULL); + if (virMutexInit(&pool->lock) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(pool); + return NULL; + } virStoragePoolObjLock(pool); pool->active = 0; pool->def = def; if (VIR_REALLOC_N(pools->objs, pools->count+1) < 0) { pool->def = NULL; + virStoragePoolObjUnlock(pool); virStoragePoolObjFree(pool); virStorageReportError(conn, VIR_ERR_NO_MEMORY, NULL); return NULL; @@ -1530,23 +1539,12 @@ char *virStoragePoolSourceListFormat(vir } -#ifdef HAVE_PTHREAD_H - void virStoragePoolObjLock(virStoragePoolObjPtr obj) { - pthread_mutex_lock(&obj->lock); + virMutexLock(&obj->lock); } void virStoragePoolObjUnlock(virStoragePoolObjPtr obj) { - pthread_mutex_unlock(&obj->lock); + virMutexUnlock(&obj->lock); } -#else -void virStoragePoolObjLock(virStoragePoolObjPtr obj ATTRIBUTE_UNUSED) -{ -} - -void virStoragePoolObjUnlock(virStoragePoolObjPtr obj ATTRIBUTE_UNUSED) -{ -} -#endif diff --git a/src/storage_conf.h b/src/storage_conf.h --- a/src/storage_conf.h +++ b/src/storage_conf.h @@ -26,6 +26,7 @@ #include "internal.h" #include "util.h" +#include "threads.h" /* Shared structs */ @@ -223,7 +224,7 @@ typedef struct _virStoragePoolObj virSto typedef virStoragePoolObj *virStoragePoolObjPtr; struct _virStoragePoolObj { - PTHREAD_MUTEX_T(lock); + virMutex lock; char *configFile; char *autostartLink; @@ -250,7 +251,7 @@ typedef struct _virStorageDriverState vi typedef virStorageDriverState *virStorageDriverStatePtr; struct _virStorageDriverState { - PTHREAD_MUTEX_T(lock); + virMutex lock; virStoragePoolObjList pools; diff --git a/src/storage_driver.c b/src/storage_driver.c --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -49,11 +49,11 @@ static int storageDriverShutdown(void); static void storageDriverLock(virStorageDriverStatePtr driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void storageDriverUnlock(virStorageDriverStatePtr driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } static void @@ -113,7 +113,10 @@ storageDriverStartup(void) { if (VIR_ALLOC(driverState) < 0) return -1; - pthread_mutex_init(&driverState->lock, NULL); + if (virMutexInit(&driverState->lock) < 0) { + VIR_FREE(driverState); + return -1; + } storageDriverLock(driverState); if (!uid) { @@ -266,6 +269,7 @@ storageDriverShutdown(void) { VIR_FREE(driverState->configDir); VIR_FREE(driverState->autostartDir); storageDriverUnlock(driverState); + virMutexDestroy(&driverState->lock); VIR_FREE(driverState); return 0; diff --git a/src/test.c b/src/test.c --- a/src/test.c +++ b/src/test.c @@ -44,6 +44,7 @@ #include "domain_conf.h" #include "storage_conf.h" #include "xml.h" +#include "threads.h" #define MAX_CPUS 128 @@ -58,7 +59,7 @@ typedef struct _testCell *testCellPtr; #define MAX_CELLS 128 struct _testConn { - PTHREAD_MUTEX_T(lock); + virMutex lock; char path[PATH_MAX]; int nextDomID; @@ -93,20 +94,15 @@ static const virNodeInfo defaultNodeInfo virReportErrorHelper(conn, VIR_FROM_TEST, code, __FILE__, \ __FUNCTION__, __LINE__, fmt) -#ifdef HAVE_THREAD_H static void testDriverLock(testConnPtr driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void testDriverUnlock(testConnPtr driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } -#else -static void testDriverLock(testConnPtr driver ATTRIBUTE_UNUSED) {} -static void testDriverUnlock(testConnPtr driver ATTRIBUTE_UNUSED) {} -#endif static virCapsPtr testBuildCapabilities(virConnectPtr conn) { @@ -216,9 +212,15 @@ static int testOpenDefault(virConnectPtr testError(conn, VIR_ERR_NO_MEMORY, "testConn"); return VIR_DRV_OPEN_ERROR; } + if (virMutexInit(&privconn->lock) < 0) { + testError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(privconn); + return VIR_DRV_OPEN_ERROR; + } + + testDriverLock(privconn); conn->privateData = privconn; - pthread_mutex_init(&privconn->lock, NULL); - testDriverLock(privconn); if (gettimeofday(&tv, NULL) < 0) { testError(NULL, VIR_ERR_INTERNAL_ERROR, "%s", _("getting time of day")); @@ -282,6 +284,7 @@ static int testOpenDefault(virConnectPtr virStoragePoolObjUnlock(poolobj); testDriverUnlock(privconn); + return VIR_DRV_OPEN_SUCCESS; error: @@ -290,6 +293,7 @@ error: virStoragePoolObjListFree(&privconn->pools); virCapabilitiesFree(privconn->caps); testDriverUnlock(privconn); + conn->privateData = NULL; VIR_FREE(privconn); return VIR_DRV_OPEN_ERROR; } @@ -335,9 +339,15 @@ static int testOpenFromFile(virConnectPt testError(NULL, VIR_ERR_NO_MEMORY, "testConn"); return VIR_DRV_OPEN_ERROR; } + if (virMutexInit(&privconn->lock) < 0) { + testError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot initialize mutex")); + VIR_FREE(privconn); + return VIR_DRV_OPEN_ERROR; + } + + testDriverLock(privconn); conn->privateData = privconn; - pthread_mutex_init(&privconn->lock, NULL); - testDriverLock(privconn); if (!(privconn->caps = testBuildCapabilities(conn))) goto error; @@ -643,6 +653,7 @@ static int testClose(virConnectPtr conn) virNetworkObjListFree(&privconn->networks); virStoragePoolObjListFree(&privconn->pools); testDriverUnlock(privconn); + virMutexDestroy(&privconn->lock); VIR_FREE (privconn); conn->privateData = NULL; diff --git a/src/threads-pthread.c b/src/threads-pthread.c new file mode 100644 --- /dev/null +++ b/src/threads-pthread.c @@ -0,0 +1,117 @@ +/* + * threads-pthread.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> + + +/* Nothing special required for pthreads */ +int virThreadInitialize(void) +{ + return 0; +} + +void virThreadOnExit(void) +{ +} + + +int virMutexInit(virMutexPtr m) +{ + if (pthread_mutex_init(&m->lock, NULL) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void virMutexDestroy(virMutexPtr m) +{ + pthread_mutex_destroy(&m->lock); +} + +void virMutexLock(virMutexPtr m){ + pthread_mutex_lock(&m->lock); +} + +void virMutexUnlock(virMutexPtr m) +{ + pthread_mutex_unlock(&m->lock); +} + + + +int virCondInit(virCondPtr c) +{ + if (pthread_cond_init(&c->cond, NULL) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +int virCondDestroy(virCondPtr c) +{ + if (pthread_cond_destroy(&c->cond) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +int virCondWait(virCondPtr c, virMutexPtr m) +{ + if (pthread_cond_wait(&c->cond, &m->lock) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void virCondSignal(virCondPtr c) +{ + pthread_cond_signal(&c->cond); +} + +void virCondBroadcast(virCondPtr c) +{ + pthread_cond_broadcast(&c->cond); +} + + +int virThreadLocalInit(virThreadLocalPtr l, + virThreadLocalCleanup c) +{ + if (pthread_key_create(&l->key, c) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +void *virThreadLocalGet(virThreadLocalPtr l) +{ + return pthread_getspecific(l->key); +} + +void virThreadLocalSet(virThreadLocalPtr l, void *val) +{ + pthread_setspecific(l->key, val); +} diff --git a/src/threads-pthread.h b/src/threads-pthread.h new file mode 100644 --- /dev/null +++ b/src/threads-pthread.h @@ -0,0 +1,36 @@ +/* + * threads.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" + +#include <pthread.h> + +struct virMutex { + pthread_mutex_t lock; +}; + +struct virCond { + pthread_cond_t cond; +}; + +struct virThreadLocal { + pthread_key_t key; +}; diff --git a/src/threads-win32.c b/src/threads-win32.c new file mode 100644 --- /dev/null +++ b/src/threads-win32.c @@ -0,0 +1,223 @@ +/* + * threads-win32.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> + +#include "memory.h" + +struct virThreadLocalData { + DWORD key; + virThreadLocalCleanup cleanup; +}; +typedef struct virThreadLocalData virThreadLocalData; +typedef virThreadLocalData *virThreadLocalDataPtr; + +virMutex virThreadLocalLock; +unsigned int virThreadLocalCount = 0; +virThreadLocalDataPtr virThreadLocalList = NULL; + + +virThreadLocal virCondEvent; + +void virCondEventCleanup(void *data); + +int virThreadInitialize(void) +{ + virMutexInit(&virThreadLocalLock); + virThreadLocalInit(&virCondEvent, virCondEventCleanup); + + return 0; +} + +void virThreadOnExit(void) +{ + unsigned int i; + virMutexLock(&virThreadLocalLock); + for (i = 0 ; i < virThreadLocalCount ; i++) { + if (virThreadLocalList[i].cleanup) { + void *data = TlsGetValue(virThreadLocalList[i].key); + if (data) { + TlsSetValue(virThreadLocalList[i].key, NULL); + + (virThreadLocalList[i].cleanup)(data); + } + } + } + virMutexUnlock(&virThreadLocalLock); +} + + +int virMutexInit(virMutexPtr m) +{ + if (!(m->lock = CreateMutex(NULL, FALSE, NULL))) { + errno = ESRCH; + return -1; + } + return 0; +} + +void virMutexDestroy(virMutexPtr m) +{ + CloseHandle(m->lock); +} + +void virMutexLock(virMutexPtr m) +{ + WaitForSingleObject(m->lock, INFINITE); +} + +void virMutexUnlock(virMutexPtr m) +{ + ReleaseMutex(m->lock); +} + + + +int virCondInit(virCondPtr c) +{ + c->waiters = NULL; + if (virMutexInit(&c->lock) < 0) + return -1; + return 0; +} + +int virCondDestroy(virCondPtr c) +{ + if (c->waiters) { + errno = EINVAL; + return -1; + } + virMutexDestroy(&c->lock); + return 0; +} + +void virCondEventCleanup(void *data) +{ + HANDLE event = data; + CloseHandle(event); +} + +int virCondWait(virCondPtr c, virMutexPtr m) +{ + HANDLE event = virThreadLocalGet(&virCondEvent); + + if (!event) { + event = CreateEvent(0, FALSE, FALSE, NULL); + if (!event) { + return -1; + } + virThreadLocalSet(&virCondEvent, event); + } + + virMutexLock(&c->lock); + + if (VIR_REALLOC_N(c->waiters, c->nwaiters + 1) < 0) { + virMutexUnlock(&c->lock); + return -1; + } + c->waiters[c->nwaiters] = event; + c->nwaiters++; + + virMutexUnlock(&c->lock); + + virMutexUnlock(m); + + if (WaitForSingleObject(event, INFINITE) == WAIT_FAILED) { + virMutexLock(m); + errno = EINVAL; + return -1; + } + + virMutexLock(m); + return 0; +} + +void virCondSignal(virCondPtr c) +{ + virMutexLock(&c->lock); + + if (c->nwaiters) { + HANDLE event = c->waiters[0]; + if (c->nwaiters > 1) + memmove(c->waiters, + c->waiters + 1, + sizeof(c->waiters[0]) * (c->nwaiters-1)); + if (VIR_REALLOC_N(c->waiters, c->nwaiters - 1) < 0) { + ; + } + c->nwaiters--; + SetEvent(event); + } + + virMutexUnlock(&c->lock); +} + +void virCondBroadcast(virCondPtr c) +{ + virMutexLock(&c->lock); + + if (c->nwaiters) { + unsigned int i; + for (i = 0 ; i < c->nwaiters ; i++) { + HANDLE event = c->waiters[i]; + SetEvent(event); + } + VIR_FREE(c->waiters); + c->nwaiters = 0; + } + + virMutexUnlock(&c->lock); +} + + + +int virThreadLocalInit(virThreadLocalPtr l, + virThreadLocalCleanup c) +{ + if ((l->key = TlsAlloc()) == TLS_OUT_OF_INDEXES) { + errno = ESRCH; + return -1; + } + TlsSetValue(l->key, NULL); + + if (c) { + virMutexLock(&virThreadLocalLock); + if (VIR_REALLOC_N(virThreadLocalList, + virThreadLocalCount + 1) < 0) + return -1; + virThreadLocalList[virThreadLocalCount].key = l->key; + virThreadLocalList[virThreadLocalCount].cleanup = c; + virThreadLocalCount++; + virMutexUnlock(&virThreadLocalLock); + } + + return 0; +} + +void *virThreadLocalGet(virThreadLocalPtr l) +{ + return TlsGetValue(l->key); +} + +void virThreadLocalSet(virThreadLocalPtr l, void *val) +{ + TlsSetValue(l->key, val); +} diff --git a/src/threads-win32.h b/src/threads-win32.h new file mode 100644 --- /dev/null +++ b/src/threads-win32.h @@ -0,0 +1,39 @@ +/* + * threads-win32.h basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" + +#include <windows.h> + +struct virMutex { + HANDLE lock; +}; + +struct virCond { + virMutex lock; + unsigned int nwaiters; + HANDLE *waiters; +}; + + +struct virThreadLocal { + DWORD key; +}; diff --git a/src/threads.c b/src/threads.c new file mode 100644 --- /dev/null +++ b/src/threads.c @@ -0,0 +1,34 @@ +/* + * threads.c: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <config.h> + +#include "threads.h" + +#ifdef HAVE_PTHREAD_H +#include "threads-pthread.c" +#else +#ifdef WIN32 +#include "threads-win32.c" +#else +#error "Either pthreads or Win32 threads are required" +#endif +#endif diff --git a/src/threads.h b/src/threads.h new file mode 100644 --- /dev/null +++ b/src/threads.h @@ -0,0 +1,72 @@ +/* + * threads.h: basic thread synchronization primitives + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __THREADS_H_ +#define __THREADS_H_ + +#include "internal.h" + +typedef struct virMutex virMutex; +typedef virMutex *virMutexPtr; + +typedef struct virCond virCond; +typedef virCond *virCondPtr; + +typedef struct virThreadLocal virThreadLocal; +typedef virThreadLocal *virThreadLocalPtr; + + +int virThreadInitialize(void) ATTRIBUTE_RETURN_CHECK; +void virThreadOnExit(void); + +int virMutexInit(virMutexPtr m) ATTRIBUTE_RETURN_CHECK; +void virMutexDestroy(virMutexPtr m); + +void virMutexLock(virMutexPtr m); +void virMutexUnlock(virMutexPtr m); + + + +int virCondInit(virCondPtr c) ATTRIBUTE_RETURN_CHECK; +int virCondDestroy(virCondPtr c) ATTRIBUTE_RETURN_CHECK; + +int virCondWait(virCondPtr c, virMutexPtr m) ATTRIBUTE_RETURN_CHECK; +void virCondSignal(virCondPtr c); +void virCondBroadcast(virCondPtr c); + + +typedef void (*virThreadLocalCleanup)(void *); +int virThreadLocalInit(virThreadLocalPtr l, + virThreadLocalCleanup c) ATTRIBUTE_RETURN_CHECK; +void *virThreadLocalGet(virThreadLocalPtr l); +void virThreadLocalSet(virThreadLocalPtr l, void*); + +#ifdef HAVE_PTHREAD_H +#include "threads-pthread.h" +#else +#ifdef WIN32 +#include "threads-win32.h" +#else +#error "Either pthreads or Win32 threads are required" +#endif +#endif + +#endif diff --git a/src/uml_conf.h b/src/uml_conf.h --- a/src/uml_conf.h +++ b/src/uml_conf.h @@ -30,6 +30,7 @@ #include "network_conf.h" #include "domain_conf.h" #include "virterror_internal.h" +#include "threads.h" #define umlDebug(fmt, ...) do {} while(0) @@ -39,7 +40,7 @@ /* Main driver state */ struct uml_driver { - PTHREAD_MUTEX_T(lock); + virMutex lock; unsigned int umlVersion; int nextvmid; diff --git a/src/uml_driver.c b/src/uml_driver.c --- a/src/uml_driver.c +++ b/src/uml_driver.c @@ -74,11 +74,11 @@ static int umlShutdown(void); static void umlDriverLock(struct uml_driver *driver) { - pthread_mutex_lock(&driver->lock); + virMutexLock(&driver->lock); } static void umlDriverUnlock(struct uml_driver *driver) { - pthread_mutex_unlock(&driver->lock); + virMutexUnlock(&driver->lock); } @@ -314,7 +314,10 @@ umlStartup(void) { if (VIR_ALLOC(uml_driver) < 0) return -1; - pthread_mutex_init(¨_driver->lock, NULL); + if (virMutexInit(¨_driver->lock) < 0) { + VIR_FREE(uml_driver); + return -1; + } umlDriverLock(uml_driver); /* Don't have a dom0 so start from 1 */ @@ -501,6 +504,7 @@ umlShutdown(void) { brShutdown(uml_driver->brctl); umlDriverUnlock(uml_driver); + virMutexDestroy(¨_driver->lock); VIR_FREE(uml_driver); return 0; diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -6500,6 +6500,7 @@ _vshStrdup(vshControl *ctl, const char * /* * Initialize connection. */ + static int vshInit(vshControl *ctl) { -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list