1 /* $OpenBSD: futex.c,v 1.5 2021/11/22 18:42:16 kettenis Exp $ */
2 /*
3 * Copyright (c) 2017 Martin Pieuchot
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/time.h>
19 #include <sys/futex.h>
20 #include <sys/mman.h>
21 #include <sys/wait.h>
22
23 #include <assert.h>
24 #include <errno.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "futex.h"
32
33 uint32_t lock = 0;
34 uint32_t *shlock;
35
36 void handler(int);
37 void *signaled(void *);
38 void *awakener(void *);
39
40 int
main(int argc,char * argv[])41 main(int argc, char *argv[])
42 {
43 char filename[] = "/tmp/futex.XXXXXXXX";
44 struct sigaction sa;
45 struct timespec tmo = { 0, 5000 };
46 pthread_t thread;
47 pid_t pid;
48 int fd, i, status;
49
50 /* Invalid operation */
51 assert(futex(&lock, 0xFFFF, 0, 0, NULL) == -1);
52 assert(errno == ENOSYS);
53
54 /* Incorrect pointer */
55 assert(futex_twait((void *)0xdeadbeef, 1, 0, NULL, 0) == -1);
56 assert(errno == EFAULT);
57
58 /* If (lock != 1) return EAGAIN */
59 assert(futex_twait(&lock, 1, 0, NULL, 0) == -1);
60 assert(errno == EAGAIN);
61
62 /* Deadlock for 5000ns */
63 assert(futex_twait(&lock, 0, CLOCK_REALTIME, &tmo, 0) == -1);
64 assert(errno == ETIMEDOUT);
65
66 /* Interrupt a thread waiting on a futex. */
67 memset(&sa, 0, sizeof(sa));
68 sa.sa_handler = handler;
69 assert(sigaction(SIGUSR1, &sa, NULL) == 0);
70 assert(pthread_create(&thread, NULL, signaled, NULL) == 0);
71 usleep(100);
72 assert(pthread_kill(thread, SIGUSR1) == 0);
73 assert(pthread_join(thread, NULL) == 0);
74
75 /* Wait until another thread awakes us. */
76 assert(pthread_create(&thread, NULL, awakener, NULL) == 0);
77 assert(futex_twait(&lock, 0, 0, NULL, 0) == 0);
78 assert(pthread_join(thread, NULL) == 0);
79
80 /* Create a uvm object for sharing a lock. */
81 fd = mkstemp(filename);
82 assert(fd != -1);
83 unlink(filename);
84 assert(ftruncate(fd, 65536) == 0);
85 shlock = mmap(NULL, sizeof(*shlock), PROT_READ | PROT_WRITE,
86 MAP_SHARED, fd, 0);
87 assert(shlock != MAP_FAILED);
88 close(fd);
89
90 /* Wake another process. */
91 pid = fork();
92 assert(pid != -1);
93 if (pid == 0) {
94 usleep(50000);
95 futex_wake(shlock, -1, 0);
96 _exit(0);
97 } else {
98 assert(futex_twait(shlock, 0, 0, NULL, 0) == 0);
99 assert(waitpid(pid, &status, 0) == pid);
100 assert(WIFEXITED(status));
101 assert(WEXITSTATUS(status) == 0);
102 }
103
104 /* Cannot wake another process using a private futex. */
105 for (i = 1; i < 4; i++) {
106 pid = fork();
107 assert(pid != -1);
108 if (pid == 0) {
109 usleep(50000);
110 futex_wake(shlock, -1,
111 (i & 1) ? FUTEX_PRIVATE_FLAG : 0);
112 _exit(0);
113 } else {
114 tmo.tv_sec = 0;
115 tmo.tv_nsec = 200000000;
116 assert(futex_twait(shlock, 0, CLOCK_REALTIME, &tmo,
117 (i & 2) ? FUTEX_PRIVATE_FLAG : 0) == -1);
118 assert(errno == ETIMEDOUT);
119 assert(waitpid(pid, &status, 0) == pid);
120 assert(WIFEXITED(status));
121 assert(WEXITSTATUS(status) == 0);
122 }
123 }
124
125 assert(munmap(shlock, sizeof(*shlock)) == 0);
126
127 /* Create anonymous memory for sharing a lock. */
128 shlock = mmap(NULL, sizeof(*shlock), PROT_READ | PROT_WRITE,
129 MAP_ANON | MAP_SHARED, -1, 0);
130 assert(shlock != MAP_FAILED);
131
132 /* Wake another process. */
133 pid = fork();
134 assert(pid != -1);
135 if (pid == 0) {
136 usleep(50000);
137 futex_wake(shlock, -1, 0);
138 _exit(0);
139 } else {
140 assert(futex_twait(shlock, 0, 0, NULL, 0) == 0);
141 assert(waitpid(pid, &status, 0) == pid);
142 assert(WIFEXITED(status));
143 assert(WEXITSTATUS(status) == 0);
144 }
145
146 /* Cannot wake another process using a private futex. */
147 for (i = 1; i < 4; i++) {
148 pid = fork();
149 assert(pid != -1);
150 if (pid == 0) {
151 usleep(50000);
152 futex_wake(shlock, -1,
153 (i & 1) ? FUTEX_PRIVATE_FLAG : 0);
154 _exit(0);
155 } else {
156 tmo.tv_sec = 0;
157 tmo.tv_nsec = 200000000;
158 assert(futex_twait(shlock, 0, CLOCK_REALTIME, &tmo,
159 (i & 2) ? FUTEX_PRIVATE_FLAG : 0) == -1);
160 assert(errno == ETIMEDOUT);
161 assert(waitpid(pid, &status, 0) == pid);
162 assert(WIFEXITED(status));
163 assert(WEXITSTATUS(status) == 0);
164 }
165 }
166
167 assert(munmap(shlock, sizeof(*shlock)) == 0);
168
169 return 0;
170 }
171
172 void
handler(int sig)173 handler(int sig)
174 {
175 }
176
177 void *
signaled(void * arg)178 signaled(void *arg)
179 {
180 /* Wait until receiving a signal. */
181 assert(futex_twait(&lock, 0, 0, NULL, 0) == -1);
182 assert(errno == EINTR);
183 return NULL;
184 }
185
186 void *
awakener(void * arg)187 awakener(void *arg)
188 {
189 usleep(100);
190 assert(futex_wake(&lock, -1, 0) == 1);
191 return NULL;
192 }
193