bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: bugs in test-lock


From: Bruno Haible
Subject: Re: bugs in test-lock
Date: Fri, 06 Jan 2017 00:11:56 +0100
User-agent: KMail/4.8.5 (Linux/3.8.0-44-generic; KDE/4.8.5; x86_64; ; )

Torvald Riegel wrote:
> > The problem here is: it's a unit test. If it fails, I don't want to search
> > for bugs in the condition variable subsystem, semaphore subsystem, AND the
> > lock subsystem. I want the test to rely essentially only on the lock 
> > subsystem.
> 
> locks are the wrong mechanism.  locks give you mutual exclusion.  If you
> want a simple notification that you can query without blocking, use a
> semaphore.

OK, thanks for this clear advice. I've pushed the change below.

The timings look OK:
EXPLICIT_YIELD = 1 USE_SEMAPHORE = 1      1.79 sec
EXPLICIT_YIELD = 1 USE_SEMAPHORE = 0      1.78 sec
EXPLICIT_YIELD = 0 USE_SEMAPHORE = 1      3.3 .. 3.8 sec
EXPLICIT_YIELD = 0 USE_SEMAPHORE = 0      3.3 .. 3.9 sec

> > That's why I inserted the yield statements in the lock_checker_thread:
> > 
> >   for (;;)
> >     {
> >       pthread_mutex_lock (&lock);
> >       do something;
> >       pthread_mutex_unlock (&lock);
> >       do very little other things;
> >       pthread_yield ();
> >     }
> 
> Why do you think this works?

I think this works because
  - the manual page http://man7.org/linux/man-pages/man3/pthread_yield.3.html
    says "The thread is placed at the end of the run queue". To me, this means
    that if all participating threads call pthread_yield () once in a while,
    all threads have a chance to be woken up and get the lock within a 
reasonable
    amount of time.
  - the timings of EXPLICIT_YIELD = 1 vs. 0 (above) show that it has some
    effect.

> We don't have uniprocessors anymore,
> there's more that's affecting execution order than just the OS
> scheduler.
> ...
> There's no guarantee that this will give fairness to calls before or
> after it.  The OS scheduler may let other runnable processes run
> first, but modern synchronization primitives try not to involve the OS
> as much as possible because of the overheads this typically involves.

Are you suggesting that the pthread_yield manual page is wrong? Or that
some warning should be added to it? I'm sure Michael Kerrisk will accept inputs.

> > For a real notification mechanism, probably a pthread_cond_t would be the 
> > right
> > thing.
> 
> But you don't want to wait for a condition, you want to query whether a
> condition holds.  The latter is not supported by a condvar.

Oh. Really, "wait queue" would be a better name than "condition variable"
for this thing.

Bruno


2017-01-05  Bruno Haible  <address@hidden>

        lock tests: Prefer semaphore over mutex.
        * tests/test-lock.c (USE_SEMAPHORE): New constant.
        (struct atomic_int, init_atomic_int, get_atomic_int_value,
        set_atomic_int_value) [USE_SEMAPHORE]: Define using a POSIX semaphore.
        Suggested by Torvald Riegel <address@hidden>.

diff --git a/tests/test-lock.c b/tests/test-lock.c
index 095511e..f3da4cc 100644
--- a/tests/test-lock.c
+++ b/tests/test-lock.c
@@ -51,12 +51,20 @@
 #define EXPLICIT_YIELD 1
 
 /* Whether to use 'volatile' on some variables that communicate information
-   between threads.  If set to 0, a lock is used to protect these variables.
-   If set to 1, 'volatile' is used; this is theoretically equivalent but can
-   lead to much slower execution (e.g. 30x slower total run time on a 40-core
-   machine.  */
+   between threads.  If set to 0, a semaphore or a lock is used to protect
+   these variables.  If set to 1, 'volatile' is used; this is theoretically
+   equivalent but can lead to much slower execution (e.g. 30x slower total
+   run time on a 40-core machine), because 'volatile' does not imply any
+   synchronization/communication between different CPUs.  */
 #define USE_VOLATILE 0
 
+#if USE_POSIX_THREADS
+/* Whether to use a semaphore to communicate information between threads.
+   If set to 0, a lock is used. If set to 1, a semaphore is used.
+   Uncomment this to reduce the dependencies of this test.  */
+# define USE_SEMAPHORE 1
+#endif
+
 /* Whether to print debugging messages.  */
 #define ENABLE_DEBUGGING 0
 
@@ -97,6 +105,10 @@
 
 #include "glthread/thread.h"
 #include "glthread/yield.h"
+#if USE_SEMAPHORE
+# include <errno.h>
+# include <semaphore.h>
+#endif
 
 #if ENABLE_DEBUGGING
 # define dbgprintf printf
@@ -128,6 +140,41 @@ set_atomic_int_value (struct atomic_int *ai, int new_value)
 {
   ai->value = new_value;
 }
+#elif USE_SEMAPHORE
+/* This atomic_int implementation can only support the values 0 and 1.
+   It is initially 0 and can be set to 1 only once.  */
+struct atomic_int {
+  sem_t semaphore;
+};
+static void
+init_atomic_int (struct atomic_int *ai)
+{
+  sem_init (&ai->semaphore, 0, 0);
+}
+static int
+get_atomic_int_value (struct atomic_int *ai)
+{
+  if (sem_trywait (&ai->semaphore) == 0)
+    {
+      if (sem_post (&ai->semaphore))
+        abort ();
+      return 1;
+    }
+  else if (errno == EAGAIN)
+    return 0;
+  else
+    abort ();
+}
+static void
+set_atomic_int_value (struct atomic_int *ai, int new_value)
+{
+  if (new_value == 0)
+    /* It's already initialized with 0.  */
+    return;
+  /* To set the value 1: */
+  if (sem_post (&ai->semaphore))
+    abort ();
+}
 #else
 struct atomic_int {
   gl_lock_define (, lock)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]