Hi, Libraries that need to protect shared datastructures with locks also often need once-only initializations. And conversely, all programs that need thread-safe once-only initialization also need locking.
So I've added support for once-only execution (à la pthread_once) to lock.h and lock.c. Bruno 2005-07-16 Bruno Haible <[EMAIL PROTECTED]> * lock.h (gl_once_t): New type. (gl_once_define, gl_once): New macros. * lock.c (fresh_once): New variable. (glthread_once, glthread_once_call, glthread_once_singlethreaded): New functions. diff -c -3 -r1.1 lock.h *** lib/lock.h 18 Jul 2005 11:35:09 -0000 1.1 --- lib/lock.h 18 Jul 2005 19:51:44 -0000 *************** *** 51,56 **** --- 51,61 ---- Taking the lock: gl_recursive_lock_lock (name); Releasing the lock: gl_recursive_lock_unlock (name); De-initialization: gl_recursive_lock_destroy (name); + + Once-only execution: + Type: gl_once_t + Initializer: gl_once_define(extern, name) + Execution: gl_once (name, initfunction); */ *************** *** 91,96 **** --- 96,102 ---- # pragma weak pthread_rwlock_wrlock # pragma weak pthread_rwlock_unlock # pragma weak pthread_rwlock_destroy + # pragma weak pthread_once # pragma weak pthread_cond_init # pragma weak pthread_cond_wait # pragma weak pthread_cond_signal *************** *** 301,306 **** --- 307,334 ---- # endif + /* -------------------------- gl_once_t datatype -------------------------- */ + + typedef pthread_once_t gl_once_t; + # define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS pthread_once_t NAME = PTHREAD_ONCE_INIT; + # define gl_once(NAME, INITFUNCTION) \ + do \ + { \ + if (pthread_in_use ()) \ + { \ + if (pthread_once (&NAME, INITFUNCTION) != 0) \ + abort (); \ + } \ + else \ + { \ + if (glthread_once_singlethreaded (&NAME)) \ + INITFUNCTION (); \ + } \ + } \ + while (0) + extern int glthread_once_singlethreaded (pthread_once_t *once_control); + #endif /* ========================================================================= */ *************** *** 322,327 **** --- 350,356 ---- # pragma weak pth_rwlock_init # pragma weak pth_rwlock_acquire # pragma weak pth_rwlock_release + # pragma weak pth_once # pragma weak pth_cancel # define pth_in_use() (pth_cancel != NULL) *************** *** 383,388 **** --- 412,441 ---- # define gl_recursive_lock_destroy(NAME) \ (void)(&NAME) + /* -------------------------- gl_once_t datatype -------------------------- */ + + typedef pth_once_t gl_once_t; + # define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS pth_once_t NAME = PTH_ONCE_INIT; + # define gl_once(NAME, INITFUNCTION) \ + do \ + { \ + if (pth_in_use ()) \ + { \ + void (*gl_once_temp) (void) = INITFUNCTION; \ + if (!pth_once (&NAME, glthread_once_call, &gl_once_temp)) \ + abort (); \ + } \ + else \ + { \ + if (glthread_once_singlethreaded (&NAME)) \ + INITFUNCTION (); \ + } \ + } \ + while (0) + extern void glthread_once_call (void *arg); + extern int glthread_once_singlethreaded (pth_once_t *once_control); + #endif /* ========================================================================= */ *************** *** 482,487 **** --- 535,567 ---- extern void glthread_recursive_lock_unlock (gl_recursive_lock_t *lock); extern void glthread_recursive_lock_destroy (gl_recursive_lock_t *lock); + /* -------------------------- gl_once_t datatype -------------------------- */ + + typedef struct + { + volatile int inited; + mutex_t mutex; + } + gl_once_t; + # define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_once_t NAME = { 0, DEFAULTMUTEX }; + # define gl_once(NAME, INITFUNCTION) \ + do \ + { \ + if (thread_in_use ()) \ + { \ + glthread_once (&NAME, INITFUNCTION); \ + } \ + else \ + { \ + if (glthread_once_singlethreaded (&NAME)) \ + INITFUNCTION (); \ + } \ + } \ + while (0) + extern void glthread_once (gl_once_t *once_control, void (*initfunction) (void)); + extern int glthread_once_singlethreaded (gl_once_t *once_control); + #endif /* ========================================================================= */ *************** *** 602,607 **** --- 682,702 ---- extern void glthread_recursive_lock_unlock (gl_recursive_lock_t *lock); extern void glthread_recursive_lock_destroy (gl_recursive_lock_t *lock); + /* -------------------------- gl_once_t datatype -------------------------- */ + + typedef struct + { + volatile int inited; + volatile long started; + CRITICAL_SECTION lock; + } + gl_once_t; + # define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_once_t NAME = { -1, -1 }; + # define gl_once(NAME, INITFUNCTION) \ + glthread_once (&NAME, INITFUNCTION) + extern void glthread_once (gl_once_t *once_control, void (*initfunction) (void)); + #endif /* ========================================================================= */ *************** *** 637,641 **** --- 732,752 ---- # define gl_recursive_lock_init(NAME) # define gl_recursive_lock_lock(NAME) # define gl_recursive_lock_unlock(NAME) + + /* -------------------------- gl_once_t datatype -------------------------- */ + + typedef int gl_once_t; + # define gl_once_define(STORAGECLASS, NAME) \ + STORAGECLASS gl_once_t NAME = 0; + # define gl_once(NAME, INITFUNCTION) \ + do \ + { \ + if (NAME == 0) \ + { \ + NAME = ~ 0; \ + INITFUNCTION (); \ + } \ + } \ + while (0) #endif diff -c -3 -r1.1 lock.c *** lib/lock.c 18 Jul 2005 11:35:09 -0000 1.1 --- lib/lock.c 18 Jul 2005 19:51:44 -0000 *************** *** 322,327 **** --- 322,347 ---- # endif + /* -------------------------- gl_once_t datatype -------------------------- */ + + static const pthread_once_t fresh_once = PTHREAD_ONCE_INIT; + + int + glthread_once_singlethreaded (pthread_once_t *once_control) + { + /* We don't know whether pthread_once_t is an integer type, a floating-point + type, a pointer type, or a structure type. */ + char *firstbyte = (char *)once_control; + if (*firstbyte == *(const char *)&fresh_once) + { + /* First time use of once_control. Invert the first byte. */ + *firstbyte = ~ *(const char *)&fresh_once; + return 1; + } + else + return 0; + } + #endif /* ========================================================================= */ *************** *** 336,341 **** --- 356,385 ---- /* --------------------- gl_recursive_lock_t datatype --------------------- */ + /* -------------------------- gl_once_t datatype -------------------------- */ + + void + glthread_once_call (void *arg) + { + void (**gl_once_temp_addr) (void) = (void (**) (void)) arg; + void (*initfunction) (void) = *gl_once_temp_addr; + initfunction (); + } + + int + glthread_once_singlethreaded (pth_once_t *once_control) + { + /* We know that pth_once_t is an integer type. */ + if (*once_control == PTH_ONCE_INIT) + { + /* First time use of once_control. Invert the marker. */ + *once_control = ~ PTH_ONCE_INIT; + return 1; + } + else + return 0; + } + #endif /* ========================================================================= */ *************** *** 397,402 **** --- 441,481 ---- abort (); } + /* -------------------------- gl_once_t datatype -------------------------- */ + + void + glthread_once (gl_once_t *once_control, void (*initfunction) (void)) + { + if (!once_control->inited) + { + /* Use the mutex to guarantee that if another thread is already calling + the initfunction, this thread waits until it's finished. */ + if (mutex_lock (&once_control->mutex) != 0) + abort (); + if (!once_control->inited) + { + once_control->inited = 1; + initfunction (); + } + if (mutex_unlock (&once_control->mutex) != 0) + abort (); + } + } + + int + glthread_once_singlethreaded (gl_once_t *once_control) + { + /* We know that gl_once_t contains an integer type. */ + if (!once_control->inited) + { + /* First time use of once_control. Invert the marker. */ + once_control->inited = ~ 0; + return 1; + } + else + return 0; + } + #endif /* ========================================================================= */ *************** *** 762,767 **** --- 841,885 ---- abort (); DeleteCriticalSection (&lock->lock); lock->guard.done = 0; + } + + /* -------------------------- gl_once_t datatype -------------------------- */ + + void + glthread_once (gl_once_t *once_control, void (*initfunction) (void)) + { + if (once_control->inited <= 0) + { + if (InterlockedIncrement (&once_control->started) == 0) + { + /* This thread is the first one to come to this once_control. */ + InitializeCriticalSection (&once_control->lock); + EnterCriticalSection (&once_control->lock); + once_control->inited = 0; + initfunction (); + once_control->inited = 1; + LeaveCriticalSection (&once_control->lock); + } + else + { + /* Undo last operation. */ + InterlockedDecrement (&once_control->started); + /* Some other thread has already started the initialization. + Yield the CPU while waiting for the other thread to finish + initializing and taking the lock. */ + while (once_control->inited < 0) + Sleep (0); + if (once_control->inited <= 0) + { + /* Take the lock. This blocks until the other thread has + finished calling the initfunction. */ + EnterCriticalSection (&once_control->lock); + LeaveCriticalSection (&once_control->lock); + if (!(once_control->inited > 0)) + abort (); + } + } + } } #endif _______________________________________________ bug-gnulib mailing list bug-gnulib@gnu.org http://lists.gnu.org/mailman/listinfo/bug-gnulib