File: futex.c

package info (click to toggle)
dtrace 2.0.5-1
  • links: PTS
  • area: main
  • in suites: sid
  • size: 24,408 kB
  • sloc: ansic: 61,247; sh: 17,997; asm: 1,717; lex: 947; awk: 754; yacc: 695; perl: 37; sed: 17; makefile: 15
file content (152 lines) | stat: -rw-r--r-- 3,569 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * THIS PROGRAM IS ADAPTED FROM THE futex MAN PAGE.
 *
 * 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 'num-loops' 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 'futexp': 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.
	 */

	while (1) {

		/* Is the futex available? */

		if (__sync_bool_compare_and_swap(futexp, 1, 0))
			break;      /* Yes */

		/* Futex is not available; wait */

		s = futex(futexp, FUTEX_WAIT, 0, 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,
 * 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 mapping. */

	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);
}