linux-man.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Bugs in futex(2) example - fix for deadlock/busy-waiting and output
@ 2019-10-14 18:10 Georg Sauthoff
  2019-10-25 17:35 ` Georg Sauthoff
  0 siblings, 1 reply; 2+ messages in thread
From: Georg Sauthoff @ 2019-10-14 18:10 UTC (permalink / raw)
  To: mtk.manpages; +Cc: linux-man

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"

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2019-10-25 17:45 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-14 18:10 Bugs in futex(2) example - fix for deadlock/busy-waiting and output Georg Sauthoff
2019-10-25 17:35 ` Georg Sauthoff

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).