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 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 173 handler(int sig) 174 { 175 } 176 177 void * 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 * 187 awakener(void *arg) 188 { 189 usleep(100); 190 assert(futex_wake(&lock, -1, 0) == 1); 191 return NULL; 192 } 193