diff options
-rw-r--r-- | gettext-runtime/intl/ChangeLog | 8 | ||||
-rw-r--r-- | gettext-runtime/intl/lock.c | 118 | ||||
-rw-r--r-- | gettext-runtime/intl/lock.h | 111 | ||||
-rw-r--r-- | gettext-runtime/m4/ChangeLog | 4 | ||||
-rw-r--r-- | gettext-runtime/m4/gettext.m4 | 3 | ||||
-rw-r--r-- | gettext-runtime/tests/ChangeLog | 6 | ||||
-rw-r--r-- | gettext-runtime/tests/test-lock.c | 153 |
7 files changed, 403 insertions, 0 deletions
diff --git a/gettext-runtime/intl/ChangeLog b/gettext-runtime/intl/ChangeLog index 9c9db70..04adb5a 100644 --- a/gettext-runtime/intl/ChangeLog +++ b/gettext-runtime/intl/ChangeLog @@ -1,5 +1,13 @@ 2005-07-16 Bruno Haible <bruno@clisp.org> + * 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. + +2005-07-16 Bruno Haible <bruno@clisp.org> + * lock.h: New file. * lock.c: New file. * bindtextdom.c: Include lock.h. Don't include THREAD_H. Remove diff --git a/gettext-runtime/intl/lock.c b/gettext-runtime/intl/lock.c index cbf9f11..5c14fa3 100644 --- a/gettext-runtime/intl/lock.c +++ b/gettext-runtime/intl/lock.c @@ -322,6 +322,26 @@ glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) # 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,6 +356,30 @@ glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) /* --------------------- 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,6 +441,41 @@ glthread_recursive_lock_destroy (gl_recursive_lock_t *lock) 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 /* ========================================================================= */ @@ -764,6 +843,45 @@ glthread_recursive_lock_destroy (gl_recursive_lock_t *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 /* ========================================================================= */ diff --git a/gettext-runtime/intl/lock.h b/gettext-runtime/intl/lock.h index 9ca33de..cdddf6c 100644 --- a/gettext-runtime/intl/lock.h +++ b/gettext-runtime/intl/lock.h @@ -51,6 +51,11 @@ 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,6 +96,7 @@ # 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,6 +307,28 @@ extern void glthread_recursive_lock_destroy (gl_recursive_lock_t *lock); # 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,6 +350,7 @@ extern void glthread_recursive_lock_destroy (gl_recursive_lock_t *lock); # 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,6 +412,30 @@ typedef pth_mutex_t gl_recursive_lock_t; # 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,6 +535,33 @@ extern void glthread_recursive_lock_lock (gl_recursive_lock_t *lock); 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,6 +682,21 @@ extern void glthread_recursive_lock_lock (gl_recursive_lock_t *lock); 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 /* ========================================================================= */ @@ -638,4 +733,20 @@ typedef int gl_recursive_lock_t; # 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 --git a/gettext-runtime/m4/ChangeLog b/gettext-runtime/m4/ChangeLog index 49ef094..ea280b8 100644 --- a/gettext-runtime/m4/ChangeLog +++ b/gettext-runtime/m4/ChangeLog @@ -1,5 +1,9 @@ 2005-07-16 Bruno Haible <bruno@clisp.org> + * gettext.m4 (AM_INTL_SUBDIR): Also hide the glthread_once* functions. + +2005-07-16 Bruno Haible <bruno@clisp.org> + * lock.m4: New file. * gettext.m4 (AM_INTL_SUBDIR): Remove multithreading checks. Instead require gl_LOCK and define __libc_lock_*, __libc_rwlock_*, diff --git a/gettext-runtime/m4/gettext.m4 b/gettext-runtime/m4/gettext.m4 index d8686cf..b437e03 100644 --- a/gettext-runtime/m4/gettext.m4 +++ b/gettext-runtime/m4/gettext.m4 @@ -492,6 +492,9 @@ __fsetlocking]) #define glthread_recursive_lock_lock libintl_recursive_lock_lock #define glthread_recursive_lock_unlock libintl_recursive_lock_unlock #define glthread_recursive_lock_destroy libintl_recursive_lock_destroy +#define glthread_once libintl_once +#define glthread_once_call libintl_once_call +#define glthread_once_singlethreaded libintl_once_singlethreaded ]) dnl intl/plural.c is generated from intl/plural.y. It requires bison, diff --git a/gettext-runtime/tests/ChangeLog b/gettext-runtime/tests/ChangeLog index 62b5213..e5e6ea1 100644 --- a/gettext-runtime/tests/ChangeLog +++ b/gettext-runtime/tests/ChangeLog @@ -1,5 +1,11 @@ 2005-07-16 Bruno Haible <bruno@clisp.org> + * test-lock.c (DO_TEST_ONCE): New macro. + (once_execute, once_contender_thread, test_once): New functions. + (main): Also call test_once. + +2005-07-16 Bruno Haible <bruno@clisp.org> + * tests-lock.c: New file. * Makefile.am: New file. * Makefile.msvc: New file. diff --git a/gettext-runtime/tests/test-lock.c b/gettext-runtime/tests/test-lock.c index f74eccd..e6895ec 100644 --- a/gettext-runtime/tests/test-lock.c +++ b/gettext-runtime/tests/test-lock.c @@ -48,6 +48,7 @@ #define DO_TEST_LOCK 1 #define DO_TEST_RWLOCK 1 #define DO_TEST_RECURSIVE_LOCK 1 +#define DO_TEST_ONCE 1 /* Whether to help the scheduler through explicit yield(). Uncomment this to see if the operating system has a fair scheduler. */ @@ -66,6 +67,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #if !ENABLE_LOCKING # undef USE_POSIX_THREADS @@ -498,6 +500,152 @@ test_recursive_lock (void) check_accounts (); } +/* Test once-only execution by having several threads attempt to grab a + once-only task simultaneously (triggered by releasing a read-write lock). */ + +gl_once_define(static, fresh_once) +static int ready[THREAD_COUNT]; +static gl_lock_t ready_lock[THREAD_COUNT]; +#if ENABLE_LOCKING +static gl_rwlock_t fire_signal[REPEAT_COUNT]; +#else +static volatile int fire_signal_state; +#endif +static gl_once_t once_control; +static int performed; +gl_lock_define_initialized(static, performed_lock) + +static void +once_execute (void) +{ + gl_lock_lock (performed_lock); + performed++; + gl_lock_unlock (performed_lock); +} + +static void * +once_contender_thread (void *arg) +{ + int id = (int) (long) arg; + int repeat; + + for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) + { + /* Tell the main thread that we're ready. */ + gl_lock_lock (ready_lock[id]); + ready[id] = 1; + gl_lock_unlock (ready_lock[id]); + + if (repeat == REPEAT_COUNT) + break; + + dbgprintf ("Contender %p waiting for signal for round %d\n", + gl_thread_self (), repeat); +#if ENABLE_LOCKING + /* Wait for the signal to go. */ + gl_rwlock_rdlock (fire_signal[repeat]); + /* And don't hinder the others (if the scheduler is unfair). */ + gl_rwlock_unlock (fire_signal[repeat]); +#else + /* Wait for the signal to go. */ + while (fire_signal_state <= repeat) + yield (); +#endif + dbgprintf ("Contender %p got the signal for round %d\n", + gl_thread_self (), repeat); + + /* Contend for execution. */ + gl_once (once_control, once_execute); + } + + return NULL; +} + +void +test_once (void) +{ + int i, repeat; + gl_thread_t threads[THREAD_COUNT]; + + /* Initialize all variables. */ + for (i = 0; i < THREAD_COUNT; i++) + { + ready[i] = 0; + gl_lock_init (ready_lock[i]); + } +#if ENABLE_LOCKING + for (i = 0; i < REPEAT_COUNT; i++) + gl_rwlock_init (fire_signal[i]); +#else + fire_signal_state = 0; +#endif + + /* Block all fire_signals. */ + for (i = REPEAT_COUNT-1; i >= 0; i--) + gl_rwlock_wrlock (fire_signal[i]); + + /* Spawn the threads. */ + for (i = 0; i < THREAD_COUNT; i++) + threads[i] = gl_thread_create (once_contender_thread, (void *) (long) i); + + for (repeat = 0; repeat <= REPEAT_COUNT; repeat++) + { + /* Wait until every thread is ready. */ + dbgprintf ("Main thread before synchonizing for round %d\n", repeat); + for (;;) + { + int ready_count = 0; + for (i = 0; i < THREAD_COUNT; i++) + { + gl_lock_lock (ready_lock[i]); + ready_count += ready[i]; + gl_lock_unlock (ready_lock[i]); + } + if (ready_count == THREAD_COUNT) + break; + yield (); + } + dbgprintf ("Main thread after synchonizing for round %d\n", repeat); + + if (repeat > 0) + { + /* Check that exactly one thread executed the once_execute() + function. */ + if (performed != 1) + abort (); + } + + if (repeat == REPEAT_COUNT) + break; + + /* Preparation for the next round: Initialize once_control. */ + memcpy (&once_control, &fresh_once, sizeof (gl_once_t)); + + /* Preparation for the next round: Reset the performed counter. */ + performed = 0; + + /* Preparation for the next round: Reset the ready flags. */ + for (i = 0; i < THREAD_COUNT; i++) + { + gl_lock_lock (ready_lock[i]); + ready[i] = 0; + gl_lock_unlock (ready_lock[i]); + } + + /* Signal all threads simultaneously. */ + dbgprintf ("Main thread giving signal for round %d\n", repeat); +#if ENABLE_LOCKING + gl_rwlock_unlock (fire_signal[repeat]); +#else + fire_signal_state = repeat + 1; +#endif + } + + /* Wait for the threads to terminate. */ + for (i = 0; i < THREAD_COUNT; i++) + gl_thread_join (threads[i]); +} + int main () { @@ -521,6 +669,11 @@ main () test_recursive_lock (); printf (" OK\n"); fflush (stdout); #endif +#if DO_TEST_ONCE + printf ("Starting test_once ..."); fflush (stdout); + test_once (); + printf (" OK\n"); fflush (stdout); +#endif return 0; } |