mirror of https://github.com/mkerrisk/man-pages
futex.2: Add EXAMPLE program
Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
This commit is contained in:
parent
a4a529e85b
commit
305cc4153b
187
man2/futex.2
187
man2/futex.2
|
@ -1418,6 +1418,193 @@ This system call is Linux-specific.
|
||||||
.SH NOTES
|
.SH NOTES
|
||||||
Glibc does not provide a wrapper for this system call; call it using
|
Glibc does not provide a wrapper for this system call; call it using
|
||||||
.BR syscall (2).
|
.BR syscall (2).
|
||||||
|
.\"
|
||||||
|
.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
|
.\"
|
||||||
|
.SH EXAMPLE
|
||||||
|
.\" FIXME Is it worth having an example program?
|
||||||
|
.\" FIXME Anything obviously broken in the example program?
|
||||||
|
.\"
|
||||||
|
The program below deomonstrates use of futexes in a program
|
||||||
|
where parent and child use a pair of futexes located inside a
|
||||||
|
shared anonymous mapping to synchronize access to a shared resource:
|
||||||
|
the terminal.
|
||||||
|
The two processes each write
|
||||||
|
.IR nloops
|
||||||
|
(a command-line argument that defaults to 5 if omitted)
|
||||||
|
messages to the terminal and employ a synchronization protocol
|
||||||
|
that ensures that they alternate in writing messages.
|
||||||
|
Upon running this program we see output such as the following:
|
||||||
|
|
||||||
|
.in +4n
|
||||||
|
.nf
|
||||||
|
$ \fB./futex_demo\fP
|
||||||
|
Parent (18534) 0
|
||||||
|
Child (18535) 0
|
||||||
|
Parent (18534) 1
|
||||||
|
Child (18535) 1
|
||||||
|
Parent (18534) 2
|
||||||
|
Child (18535) 2
|
||||||
|
Parent (18534) 3
|
||||||
|
Child (18535) 3
|
||||||
|
Parent (18534) 4
|
||||||
|
Child (18535) 4
|
||||||
|
.fi
|
||||||
|
.in
|
||||||
|
.SS Program source
|
||||||
|
\&
|
||||||
|
.nf
|
||||||
|
/* futex_demo.c
|
||||||
|
|
||||||
|
Usage: futex_demo [nloops]
|
||||||
|
(Default: 5)
|
||||||
|
|
||||||
|
Demonstrate the use of futexes in a program where parent and child
|
||||||
|
use a pair of futexes located inside a shared anonymous mapping to
|
||||||
|
synchronize access to a shared resource: the terminal. The two
|
||||||
|
processes each write \(aqnum\-loops\(aq messages to the terminal and employ
|
||||||
|
a synchronization protocol that ensures that they alternate in
|
||||||
|
writing messages.
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <linux/futex.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static int *futex1, *futex2, *iaddr;
|
||||||
|
|
||||||
|
static int
|
||||||
|
futex(int *uaddr, int futex_op, int val,
|
||||||
|
const struct timespec *timeout, int *uaddr2, int val3)
|
||||||
|
{
|
||||||
|
return syscall(SYS_futex, uaddr, futex_op, val,
|
||||||
|
timeout, uaddr, val3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Acquire the futex pointed to by \(aqfutexp\(aq: wait for its value to
|
||||||
|
become 1, and then set the value to 0. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
fwait(int *futexp)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
/* __sync_bool_compare_and_swap(ptr, oldval, newval) is a gcc
|
||||||
|
built\-in function. It atomically performs the equivalent of:
|
||||||
|
|
||||||
|
if (*ptr == oldval)
|
||||||
|
*ptr = newval;
|
||||||
|
|
||||||
|
It returns true if the test yielded true and *ptr was updated.
|
||||||
|
The alternative here would be to employ the equivalent atomic
|
||||||
|
machine\-language instructions. For further information, see
|
||||||
|
the GCC Manual. */
|
||||||
|
|
||||||
|
/* Maybe the futex is already available: */
|
||||||
|
|
||||||
|
if (__sync_bool_compare_and_swap(futexp, 1, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* No; we must wait for the futex value to be changed */
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0);
|
||||||
|
if (s == \-1 && errno != EAGAIN)
|
||||||
|
errExit("futex\-FUTEX_WAIT");
|
||||||
|
|
||||||
|
/* Is the futex now available? */
|
||||||
|
|
||||||
|
if (__sync_bool_compare_and_swap(futexp, 1, 0))
|
||||||
|
break; /* Yes */
|
||||||
|
|
||||||
|
/* Futex is still not available; wait again */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release the futex pointed to by \(aqfutexp\(aq: if the futex currently
|
||||||
|
has the value 0, set its value to 1 and the wake any futex waiters,
|
||||||
|
so that if the peer is blocked in fpost(), it can proceed. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
fpost(int *futexp)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
/* __sync_bool_compare_and_swap() was described in comments above */
|
||||||
|
|
||||||
|
if (__sync_bool_compare_and_swap(futexp, 0, 1)) {
|
||||||
|
|
||||||
|
s = futex(futexp, FUTEX_WAKE, 1, NULL, NULL, 0);
|
||||||
|
if (s == \-1)
|
||||||
|
errExit("futex\-FUTEX_WAKE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
pid_t childPid;
|
||||||
|
int j, nloops;
|
||||||
|
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
|
||||||
|
nloops = (argc > 1) ? atoi(argv[1]) : 5;
|
||||||
|
|
||||||
|
/* Create a shared anonymous mapping that will hold the futexes.
|
||||||
|
Since the futexes are being shared between processes, we
|
||||||
|
subsequently use the "shared" futex operations (i.e., not the
|
||||||
|
ones suffixed "_PRIVATE") */
|
||||||
|
|
||||||
|
iaddr = mmap(NULL, sizeof(int) * 2, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_ANONYMOUS | MAP_SHARED, \-1, 0);
|
||||||
|
if (iaddr == MAP_FAILED)
|
||||||
|
errExit("mmap");
|
||||||
|
|
||||||
|
futex1 = &iaddr[0];
|
||||||
|
futex2 = &iaddr[1];
|
||||||
|
|
||||||
|
*futex1 = 0; /* State: unavailable */
|
||||||
|
*futex2 = 1; /* State: available */
|
||||||
|
|
||||||
|
/* Create a child process that inherits the shared anonymous
|
||||||
|
mappping */
|
||||||
|
|
||||||
|
childPid = fork();
|
||||||
|
if (childPid == 1)
|
||||||
|
errExit("fork");
|
||||||
|
|
||||||
|
if (childPid == 0) { /* Child */
|
||||||
|
for (j = 0; j < nloops; j++) {
|
||||||
|
fwait(futex1);
|
||||||
|
printf("Child (%ld) %d\\n", (long) getpid(), j);
|
||||||
|
fpost(futex2);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parent falls through to here */
|
||||||
|
|
||||||
|
for (j = 0; j < nloops; j++) {
|
||||||
|
fwait(futex2);
|
||||||
|
printf("Parent (%ld) %d\\n", (long) getpid(), j);
|
||||||
|
fpost(futex1);
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(NULL);
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
.fi
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.ad l
|
.ad l
|
||||||
.BR get_robust_list (2),
|
.BR get_robust_list (2),
|
||||||
|
|
Loading…
Reference in New Issue