* 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
* Re: Bugs in futex(2) example - fix for deadlock/busy-waiting and output
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
0 siblings, 0 replies; 2+ messages in thread
From: Georg Sauthoff @ 2019-10-25 17:35 UTC (permalink / raw)
To: mtk.manpages; +Cc: linux-man
On Mon, Oct 14, 2019 at 08:10:43PM +0200, Georg Sauthoff wrote:
Hello,
> I've noticed that the example in the current
> http://man7.org/linux/man-pages/man2/futex.2.html page has 2 issues:
[output,deadlock]
meanwhile, I've updated some more outdated comments and fixed const
correctness issues.
Example:
- const int zero = 0;
- if (atomic_compare_exchange_strong(futexp, &zero, 1))
- break; /* Yes */
+ int expected = 0;
+ if (atomic_compare_exchange_strong(futexp, &expected, 1))
+ break;
Since atomic_compare_exchange_strong() overwrites the expected value if
it doesn't match, declaring it as const yields undefined behavior.
Also, a reader unfamilar with atomic_compare_exchange() might wrongly
deduce that it doesn't modify its second argument.
You can find the complete modified example online:
https://gist.github.com/gsauthof/6eb6c648e483005191c37f86e759906e
And inline the updated patch:
--- futex_demo.c.orig 2019-10-14 19:36:23.292238650 +0200
+++ futex_demo.c 2019-10-20 10:13:32.268350668 +0200
@@ -36,38 +36,46 @@
}
/* Acquire the futex pointed to by 'futexp': wait for its value to
- become 1, and then set the value to 0. */
+ become 0, and then set the value to 1. */
static void
fwait(int *futexp)
{
int s;
- /* atomic_compare_exchange_strong(ptr, oldval, newval)
- atomically performs the equivalent of:
+ /* atomic_compare_exchange_strong atomically performs
+ the equivalent of:
- if (*ptr == *oldval)
- *ptr = newval;
+ bool cmpexch(int *val, int *exp, int newval)
+ {
+ if (*val == *exp) {
+ *val = newval;
+ return true;
+ } else {
+ *exp = *val;
+ return false;
+ }
+ }
- It returns true if the test yielded true and *ptr was updated. */
+ */
while (1) {
/* Is the futex available? */
- const int zero = 0;
- if (atomic_compare_exchange_strong(futexp, &zero, 1))
- break; /* Yes */
+ int expected = 0;
+ if (atomic_compare_exchange_strong(futexp, &expected, 1))
+ break;
/* 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");
}
}
/* Release the futex pointed to by 'futexp': if the futex currently
- has the value 0, set its value to 1 and the wake any futex waiters,
+ has the value 1, set its value to 0 and the wake any futex waiters,
so that if the peer is blocked in fpost(), it can proceed. */
static void
@@ -77,8 +85,8 @@
/* atomic_compare_exchange_strong() was described in comments above */
- const int one = 1;
- if (atomic_compare_exchange_strong(futexp, &one, 0)) {
+ int expected = 1;
+ if (atomic_compare_exchange_strong(futexp, &expected, 0)) {
s = futex(futexp, FUTEX_WAKE, 1, NULL, NULL, 0);
if (s == -1)
errExit("futex-FUTEX_WAKE");
@@ -108,8 +116,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 */
Best regards
Georg
^ 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).