Fix: urcu-qsbr: futex wait: handle spurious futex wakeups
[urcu.git] / src / urcu-qsbr.c
index 6ff933af4c6e1ef482c9b73b5acd133f23f41e84..5572d39da65f1030510274b0618e301e893cfbcd 100644 (file)
@@ -23,6 +23,7 @@
  * IBM's contributions to this file may be relicensed under LGPLv2 or later.
  */
 
+#define URCU_NO_COMPAT_IDENTIFIERS
 #define _LGPL_SOURCE
 #include <stdio.h>
 #include <pthread.h>
@@ -81,8 +82,7 @@ URCU_ATTR_ALIAS("urcu_qsbr_gp") extern struct urcu_gp rcu_gp_qsbr;
  * writers.
  */
 DEFINE_URCU_TLS(struct urcu_qsbr_reader, urcu_qsbr_reader);
-URCU_ATTR_ALIAS("urcu_qsbr_reader")
-extern struct urcu_qsbr_reader rcu_reader_qsbr;
+DEFINE_URCU_TLS_ALIAS(struct urcu_qsbr_reader, urcu_qsbr_reader, rcu_reader_qsbr);
 
 static CDS_LIST_HEAD(registry);
 
@@ -125,17 +125,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);
This page took 0.023523 seconds and 4 git commands to generate.