]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Add gl_once support to lock.h and lock.c.
authorBruno Haible <bruno@clisp.org>
Mon, 18 Jul 2005 11:37:15 +0000 (11:37 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:12:39 +0000 (12:12 +0200)
gettext-runtime/intl/ChangeLog
gettext-runtime/intl/lock.c
gettext-runtime/intl/lock.h
gettext-runtime/m4/ChangeLog
gettext-runtime/m4/gettext.m4
gettext-runtime/tests/ChangeLog
gettext-runtime/tests/test-lock.c

index 9c9db70f1dccc0083b164e491c4ff0ace4873dc1..04adb5a3416f633f4a1e4909739c56739f1ba261 100644 (file)
@@ -1,3 +1,11 @@
+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.
index cbf9f11632372209e12cf0477ef95230dca4da39..5c14fa3a3ef85c59d993166a065d75a64df1d800 100644 (file)
@@ -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
 
 /* ========================================================================= */
index 9ca33debcd252ac698c5b4c2a2b977df31b1dfbb..cdddf6cd43dd2ed6a685ca811129e5c1c7a8f0a3 100644 (file)
      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
index 49ef0942a3568f194743cfec37944e0e2e4d4c79..ea280b80817dc99bb5aea69cfd13021e5797fe40 100644 (file)
@@ -1,3 +1,7 @@
+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.
index d8686cf7196b57b53b1219820328cd1643a5b6c7..b437e03cb7f6813e9c1626677a3493c5fd8fb8b6 100644 (file)
@@ -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,
index 62b521368afc2634795fcbdf399669222bb85fbc..e5e6ea1ca24de92bfa9a2b00ce20e58b9da588ac 100644 (file)
@@ -1,3 +1,9 @@
+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.
index f74eccd6ab4695ecf64297d3b1180fb75754dcae..e6895ecd808bbfbe26c0174425daffed4c71ca77 100644 (file)
@@ -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;
 }