Bugs in futex(2) example - fix for deadlock/busy-waiting and output

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

 



Hello,

I've noticed that the example in the current
http://man7.org/linux/man-pages/man2/futex.2.html page has 2 issues:

1) The quoted output mismatches the actual output, i.e. the parent/child
order is reversed.

Man page output:

    $ ./futex_demo
    Parent (18534) 0
    Child  (18535) 0
    Parent (18534) 1
    Child  (18535) 1
    [..]

Actual output:

    Child  (21215) 0
    Parent (21214) 0
    Child  (21215) 1
    Parent (21214) 1
    [..]

Fix:

--- futex_demo.c.orig	2019-10-14 19:36:23.292238650 +0200
+++ futex_demo.c	2019-10-14 19:36:58.599464636 +0200
@@ -108,8 +108,8 @@
     futex1 = &iaddr[0];
     futex2 = &iaddr[1];
 
-    *futex1 = 0;        /* State: unavailable */
-    *futex2 = 1;        /* State: available */
+    *futex1 = 1;        /* State: unavailable */
+    *futex2 = 0;        /* State: available */
 
     /* Create a child process that inherits the shared anonymous
	mapping */

Note that this also fixes the comments.

2) As is, the fwait() either busy-waits or waits forever:

    static void
    fwait(int *futexp)
    {
	int s;
	while (1) {

	    /* Is the futex available? */
	    const int zero = 0;
	    if (atomic_compare_exchange_strong(futexp, &zero, 1))
		break;      /* Yes */

	    /* Futex is not available; wait */

	    s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0);

            // XXX => because 3rd arg (val) is 0 and not 1 this call
            //        likely return s==-1 and sets errno==EAGAIN
            //        (in our context)

	    if (s == -1 && errno != EAGAIN)
		errExit("futex-FUTEX_WAIT");
	}
    }

See also:

    $ strace -o log -f ./futex_demo
    $ grep 'futex.*'EAGAIN log -c
    17

The number varies, of course.

Depending on the scheduling, this also may lead to a deadlock - most easily
reproducible when running it multiple times under strace, e.g.:

    $ strace -o log -f ./futex_demo
    Parent (21488) 0
    Child  (21489) 0
    ^C
    $ 

Reason: There is a race between atomic_compare_exchange_strong() and
futex(.., FUTEX_WAIT, ..) where the first observes the futex value as 1
but the second as 0.


Fix: set val argument of futex() to 1, i.e. the same value that failed to be
set atomically:


--- futex_demo.c.orig	2019-10-14 19:36:23.292238650 +0200
+++ futex_demo.c	2019-10-14 19:49:02.696404149 +0200
@@ -60,7 +60,7 @@
 
         /* Futex is not available; wait */
 
-        s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0);
+        s = futex(futexp, FUTEX_WAIT, 1, NULL, NULL, 0);
         if (s == -1 && errno != EAGAIN)
             errExit("futex-FUTEX_WAIT");
     }

With that: no deadlocks and:

	$strace -o log -f ./futex_demo
	$ grep 'futex.*'EAGAIN log -c
	0


Best regards
Georg
-- 
Hofstadter's Law: "It always takes longer than you think it will
take, even when you take into account Hofstadter's Law"



[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux