wangzhengquan
2021-01-19 7ec14eb603babeca5fc582138f4539af7a445702
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
153
154
155
156
157
158
159
160
/* 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.
*/
#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>
#include "usg_common.h"
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */
 
 
#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;
 
  int flags, opt, fd;
  mode_t perms;
  size_t size;
 
  setbuf(stdout, NULL);
 
  nloops = (argc > 1) ? atoi(argv[1]) : 5;
 
  flags = O_RDWR | O_CREAT | O_EXCL;
  perms = S_IRUSR | S_IWUSR;
  size = sizeof(int) * 2;
 
  fd = shm_open("futex_demo", flags, perms);
  if (fd == -1)
  errExit("shm_open");
  if (ftruncate(fd, size) == -1)
  errExit("ftruncate");
 
  /* 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 = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, fd, 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);
}