Fix: urcu-qsbr: futex wait: handle spurious futex wakeups
[urcu.git] / src / urcu-qsbr.c
index e934d3b57f7681225e1577089fdd589b17a6ad33..a3382857a7f358ab2f6bd548df6504c35e8bbadf 100644 (file)
 #include <stdio.h>
 #include <pthread.h>
 #include <signal.h>
-#include <assert.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 #include <errno.h>
 #include <poll.h>
 
+#include <urcu/assert.h>
 #include <urcu/wfcqueue.h>
 #include <urcu/map/urcu-qsbr.h>
 #define BUILD_QSBR_LIB
@@ -123,17 +123,25 @@ static void wait_gp(void)
 {
        /* Read reader_gp before read futex */
        cmm_smp_rmb();
-       if (uatomic_read(&urcu_qsbr_gp.futex) != -1)
-               return;
-       while (futex_noasync(&urcu_qsbr_gp.futex, FUTEX_WAIT, -1,
-                       NULL, NULL, 0)) {
+       while (uatomic_read(&urcu_qsbr_gp.futex) == -1) {
+               if (!futex_noasync(&urcu_qsbr_gp.futex, FUTEX_WAIT, -1, NULL, NULL, 0)) {
+                       /*
+                        * Prior queued wakeups queued by unrelated code
+                        * using the same address can cause futex wait to
+                        * return 0 even through the futex value is still
+                        * -1 (spurious wakeups). Check the value again
+                        * in user-space to validate whether it really
+                        * differs from -1.
+                        */
+                       continue;
+               }
                switch (errno) {
-               case EWOULDBLOCK:
+               case EAGAIN:
                        /* Value already changed. */
                        return;
                case EINTR:
                        /* Retry if interrupted by signal. */
-                       break;  /* Get out of switch. */
+                       break;  /* Get out of switch. Check again. */
                default:
                        /* Unexpected error. */
                        urcu_die(errno);
@@ -470,10 +478,10 @@ void urcu_qsbr_thread_online(void)
 void urcu_qsbr_register_thread(void)
 {
        URCU_TLS(urcu_qsbr_reader).tid = pthread_self();
-       assert(URCU_TLS(urcu_qsbr_reader).ctr == 0);
+       urcu_posix_assert(URCU_TLS(urcu_qsbr_reader).ctr == 0);
 
        mutex_lock(&rcu_registry_lock);
-       assert(!URCU_TLS(urcu_qsbr_reader).registered);
+       urcu_posix_assert(!URCU_TLS(urcu_qsbr_reader).registered);
        URCU_TLS(urcu_qsbr_reader).registered = 1;
        cds_list_add(&URCU_TLS(urcu_qsbr_reader).node, &registry);
        mutex_unlock(&rcu_registry_lock);
@@ -487,7 +495,7 @@ void urcu_qsbr_unregister_thread(void)
         * with a waiting writer.
         */
        _urcu_qsbr_thread_offline();
-       assert(URCU_TLS(urcu_qsbr_reader).registered);
+       urcu_posix_assert(URCU_TLS(urcu_qsbr_reader).registered);
        URCU_TLS(urcu_qsbr_reader).registered = 0;
        mutex_lock(&rcu_registry_lock);
        cds_list_del(&URCU_TLS(urcu_qsbr_reader).node);
@@ -499,7 +507,7 @@ void urcu_qsbr_exit(void)
        /*
         * Assertion disabled because call_rcu threads are now rcu
         * readers, and left running at exit.
-        * assert(cds_list_empty(&registry));
+        * urcu_posix_assert(cds_list_empty(&registry));
         */
 }
 
This page took 0.024598 seconds and 4 git commands to generate.