summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gettext-runtime/intl/ChangeLog8
-rw-r--r--gettext-runtime/intl/lock.c118
-rw-r--r--gettext-runtime/intl/lock.h111
-rw-r--r--gettext-runtime/m4/ChangeLog4
-rw-r--r--gettext-runtime/m4/gettext.m43
-rw-r--r--gettext-runtime/tests/ChangeLog6
-rw-r--r--gettext-runtime/tests/test-lock.c153
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;
}