xref: /openbsd-src/regress/sys/kern/futex/futex.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: futex.c,v 1.3 2018/08/26 06:50:30 visa 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
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) == ENOSYS);
52 
53 	/* Incorrect pointer */
54 	assert(futex_twait((void *)0xdeadbeef, 1, 0, NULL, 0) == EFAULT);
55 
56 	/* If (lock != 1) return EAGAIN */
57 	assert(futex_twait(&lock, 1, 0, NULL, 0) == EAGAIN);
58 
59 	/* Deadlock for 5000ns */
60 	assert(futex_twait(&lock, 0, CLOCK_REALTIME, &tmo, 0) == ETIMEDOUT);
61 
62 	/* Interrupt a thread waiting on a futex. */
63 	memset(&sa, 0, sizeof(sa));
64 	sa.sa_handler = handler;
65 	assert(sigaction(SIGUSR1, &sa, NULL) == 0);
66 	assert(pthread_create(&thread, NULL, signaled, NULL) == 0);
67 	usleep(100);
68 	assert(pthread_kill(thread, SIGUSR1) == 0);
69 	assert(pthread_join(thread, NULL) == 0);
70 
71 	/* Wait until another thread awakes us. */
72 	assert(pthread_create(&thread, NULL, awakener, NULL) == 0);
73 	assert(futex_twait(&lock, 0, 0, NULL, 0) == 0);
74 	assert(pthread_join(thread, NULL) == 0);
75 
76 	/* Create a uvm object for sharing a lock. */
77 	fd = mkstemp(filename);
78 	assert(fd != -1);
79 	unlink(filename);
80 	assert(ftruncate(fd, 65536) == 0);
81 	shlock = mmap(NULL, sizeof(*shlock), PROT_READ | PROT_WRITE,
82 	    MAP_SHARED, fd, 0);
83 	assert(shlock != MAP_FAILED);
84 	close(fd);
85 
86 	/* Wake another process. */
87 	pid = fork();
88 	assert(pid != -1);
89 	if (pid == 0) {
90 		usleep(50000);
91 		futex_wake(shlock, -1, 0);
92 		_exit(0);
93 	} else {
94 		assert(futex_twait(shlock, 0, 0, NULL, 0) == 0);
95 		assert(waitpid(pid, &status, 0) == pid);
96 		assert(WIFEXITED(status));
97 		assert(WEXITSTATUS(status) == 0);
98 	}
99 
100 	/* Cannot wake another process using a private futex. */
101 	for (i = 1; i < 4; i++) {
102 		pid = fork();
103 		assert(pid != -1);
104 		if (pid == 0) {
105 			usleep(50000);
106 			futex_wake(shlock, -1,
107 			    (i & 1) ? FUTEX_PRIVATE_FLAG : 0);
108 			_exit(0);
109 		} else {
110 			tmo.tv_sec = 0;
111 			tmo.tv_nsec = 200000000;
112 			assert(futex_twait(shlock, 0, CLOCK_REALTIME, &tmo,
113 			    (i & 2) ? FUTEX_PRIVATE_FLAG : 0) == ETIMEDOUT);
114 			assert(waitpid(pid, &status, 0) == pid);
115 			assert(WIFEXITED(status));
116 			assert(WEXITSTATUS(status) == 0);
117 		}
118 	}
119 
120 	assert(munmap(shlock, sizeof(*shlock)) == 0);
121 
122 	return 0;
123 }
124 
125 void
126 handler(int sig)
127 {
128 }
129 
130 void *
131 signaled(void *arg)
132 {
133 	/* Wait until receiving a signal. */
134 	assert(futex_twait(&lock, 0, 0, NULL, 0) == EINTR);
135 	return NULL;
136 }
137 
138 void *
139 awakener(void *arg)
140 {
141 	usleep(100);
142 	assert(futex_wake(&lock, -1, 0) == 1);
143 	return NULL;
144 }
145