mingw lacks the counterpart to PTHREAD_MUTEX_INITIALIZER, so the best we can do is portably expose once-only runtime initialization.
* src/util/threads.h (virOnceControlPtr): New opaque type. (virOnceFunc): New callback type. (virOnce): New prototype. * src/util/threads-pthread.h (virOnceControl): Declare. (VIR_ONCE_CONTROL_INITIALIZER): Define. * src/util/threads-win32.h (virOnceControl) (VIR_ONCE_CONTROL_INITIALIZER): Likewise. * src/util/threads-pthread.c (virOnce): Implement in pthreads. * src/util/threads-win32.c (virOnce): Implement in WIN32. * src/libvirt_private.syms: Export it. --- v2: change WIN32 implementation to use lower-level primitives available to mingw and older windows, rather than higher level but newer INIT_ONCE. Meanwhile, I noticed that gnulib has an LGPLv2+ 'lock' module that provides a lot of multi-threading primitives for both pthreads, pth, and mingw; maybe we should think about rewriting threads.c in terms of gnulib and only have one implementation, rather than maintaining pthread and mingw in parallel? src/libvirt_private.syms | 1 + src/util/threads-pthread.c | 5 +++++ src/util/threads-pthread.h | 11 ++++++++++- src/util/threads-win32.c | 23 ++++++++++++++++++++++- src/util/threads-win32.h | 9 ++++++++- src/util/threads.h | 21 ++++++++++++++++++++- 6 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ba7739d..cb67861 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -866,6 +866,7 @@ virMutexInit; virMutexInitRecursive; virMutexLock; virMutexUnlock; +virOnce; virThreadCreate; virThreadID; virThreadIsSelf; diff --git a/src/util/threads-pthread.c b/src/util/threads-pthread.c index 898c4d4..82ce5c6 100644 --- a/src/util/threads-pthread.c +++ b/src/util/threads-pthread.c @@ -40,6 +40,11 @@ void virThreadOnExit(void) { } +int virOnce(virOnceControlPtr once, virOnceFunc init) +{ + return pthread_once(&once->once, init); +} + int virMutexInit(virMutexPtr m) { diff --git a/src/util/threads-pthread.h b/src/util/threads-pthread.h index b25d0c2..dcaacb7 100644 --- a/src/util/threads-pthread.h +++ b/src/util/threads-pthread.h @@ -1,7 +1,7 @@ /* * threads.c: basic thread synchronization primitives * - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009, 2011 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 @@ -38,3 +38,12 @@ struct virThread { struct virThreadLocal { pthread_key_t key; }; + +struct virOnceControl { + pthread_once_t once; +}; + +#define VIR_ONCE_CONTROL_INITIALIZER \ +{ \ + .once = PTHREAD_ONCE_INIT \ +} diff --git a/src/util/threads-win32.c b/src/util/threads-win32.c index 5661437..63f699b 100644 --- a/src/util/threads-win32.c +++ b/src/util/threads-win32.c @@ -1,7 +1,7 @@ /* * threads-win32.c: basic thread synchronization primitives * - * Copyright (C) 2009-2010 Red Hat, Inc. + * Copyright (C) 2009-2011 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 @@ -69,6 +69,27 @@ void virThreadOnExit(void) virMutexUnlock(&virThreadLocalLock); } +int virOnce(virOnceControlPtr once, virOnceFunc func) +{ + if (!once->complete) { + if (InterlockedIncrement(&once->init) == 1) { + /* We're the first thread. */ + func(); + once->complete = 1; + } else { + /* We're a later thread. Decrement the init counter back + * to avoid overflow, then yield until the first thread + * marks that the function is complete. It is rare that + * multiple threads will be waiting here, and since each + * thread is yielding except the first, we should get out + * soon enough. */ + InterlockedDecrement(&once->init); + while (!once->complete) + Sleep(0); + } + } + return 0; +} int virMutexInit(virMutexPtr m) { diff --git a/src/util/threads-win32.h b/src/util/threads-win32.h index bb7c455..29580b8 100644 --- a/src/util/threads-win32.h +++ b/src/util/threads-win32.h @@ -1,7 +1,7 @@ /* * threads-win32.h basic thread synchronization primitives * - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009, 2011 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 @@ -41,3 +41,10 @@ struct virThread { struct virThreadLocal { DWORD key; }; + +struct virOnceControl { + long init; /* 0 at startup, > 0 if init has started */ + volatile long complete; /* 0 until first thread completes callback */ +}; + +#define VIR_ONCE_CONTROL_INITIALIZER { 0, 0 } diff --git a/src/util/threads.h b/src/util/threads.h index c129301..b72610c 100644 --- a/src/util/threads.h +++ b/src/util/threads.h @@ -1,7 +1,7 @@ /* * threads.h: basic thread synchronization primitives * - * Copyright (C) 2009-2010 Red Hat, Inc. + * Copyright (C) 2009-2011 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 @@ -36,6 +36,10 @@ typedef virThreadLocal *virThreadLocalPtr; typedef struct virThread virThread; typedef virThread *virThreadPtr; +typedef struct virOnceControl virOnceControl; +typedef virOnceControl *virOnceControlPtr; + +typedef void (*virOnceFunc)(void); int virThreadInitialize(void) ATTRIBUTE_RETURN_CHECK; void virThreadOnExit(void); @@ -57,6 +61,21 @@ void virThreadJoin(virThreadPtr thread); int virThreadSelfID(void); int virThreadID(virThreadPtr thread); +/* Static initialization of mutexes is not possible, so we instead + * provide for guaranteed one-time initialization via a callback + * function. Usage: + * static virOnceControl once = VIR_ONCE_CONTROL_INITIALIZER; + * static void initializer(void) { ... } + * void myfunc() + * { + * if (virOnce(&once, initializer) < 0) + * goto error; + * ...now guaranteed that initializer has completed exactly once + * } + */ +int virOnce(virOnceControlPtr once, virOnceFunc init) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; + int virMutexInit(virMutexPtr m) ATTRIBUTE_RETURN_CHECK; int virMutexInitRecursive(virMutexPtr m) ATTRIBUTE_RETURN_CHECK; void virMutexDestroy(virMutexPtr m); -- 1.7.4.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list