1*ebff625aSkettenis /* $OpenBSD: futex.c,v 1.5 2021/11/22 18:42:16 kettenis Exp $ */
22b5b01cfSmpi /*
32b5b01cfSmpi * Copyright (c) 2017 Martin Pieuchot
42b5b01cfSmpi *
52b5b01cfSmpi * Permission to use, copy, modify, and distribute this software for any
62b5b01cfSmpi * purpose with or without fee is hereby granted, provided that the above
72b5b01cfSmpi * copyright notice and this permission notice appear in all copies.
82b5b01cfSmpi *
92b5b01cfSmpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
102b5b01cfSmpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
112b5b01cfSmpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
122b5b01cfSmpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
132b5b01cfSmpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
142b5b01cfSmpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
152b5b01cfSmpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
162b5b01cfSmpi */
172b5b01cfSmpi
182b5b01cfSmpi #include <sys/time.h>
192b5b01cfSmpi #include <sys/futex.h>
20116b6a5eSvisa #include <sys/mman.h>
21116b6a5eSvisa #include <sys/wait.h>
222b5b01cfSmpi
232b5b01cfSmpi #include <assert.h>
242b5b01cfSmpi #include <errno.h>
252b5b01cfSmpi #include <pthread.h>
262b5b01cfSmpi #include <signal.h>
27116b6a5eSvisa #include <stdlib.h>
282b5b01cfSmpi #include <string.h>
292b5b01cfSmpi #include <unistd.h>
302b5b01cfSmpi
312b5b01cfSmpi #include "futex.h"
322b5b01cfSmpi
332b5b01cfSmpi uint32_t lock = 0;
34116b6a5eSvisa uint32_t *shlock;
352b5b01cfSmpi
362b5b01cfSmpi void handler(int);
372b5b01cfSmpi void *signaled(void *);
382b5b01cfSmpi void *awakener(void *);
392b5b01cfSmpi
402b5b01cfSmpi int
main(int argc,char * argv[])412b5b01cfSmpi main(int argc, char *argv[])
422b5b01cfSmpi {
43116b6a5eSvisa char filename[] = "/tmp/futex.XXXXXXXX";
442b5b01cfSmpi struct sigaction sa;
45116b6a5eSvisa struct timespec tmo = { 0, 5000 };
462b5b01cfSmpi pthread_t thread;
47116b6a5eSvisa pid_t pid;
48116b6a5eSvisa int fd, i, status;
492b5b01cfSmpi
505b1e7562Smpi /* Invalid operation */
51f18e4490Skettenis assert(futex(&lock, 0xFFFF, 0, 0, NULL) == -1);
52f18e4490Skettenis assert(errno == ENOSYS);
535b1e7562Smpi
542b5b01cfSmpi /* Incorrect pointer */
55f18e4490Skettenis assert(futex_twait((void *)0xdeadbeef, 1, 0, NULL, 0) == -1);
56f18e4490Skettenis assert(errno == EFAULT);
572b5b01cfSmpi
582b5b01cfSmpi /* If (lock != 1) return EAGAIN */
59f18e4490Skettenis assert(futex_twait(&lock, 1, 0, NULL, 0) == -1);
60f18e4490Skettenis assert(errno == EAGAIN);
612b5b01cfSmpi
622b5b01cfSmpi /* Deadlock for 5000ns */
63f18e4490Skettenis assert(futex_twait(&lock, 0, CLOCK_REALTIME, &tmo, 0) == -1);
64f18e4490Skettenis assert(errno == ETIMEDOUT);
652b5b01cfSmpi
662b5b01cfSmpi /* Interrupt a thread waiting on a futex. */
672b5b01cfSmpi memset(&sa, 0, sizeof(sa));
682b5b01cfSmpi sa.sa_handler = handler;
692b5b01cfSmpi assert(sigaction(SIGUSR1, &sa, NULL) == 0);
702b5b01cfSmpi assert(pthread_create(&thread, NULL, signaled, NULL) == 0);
712b5b01cfSmpi usleep(100);
722b5b01cfSmpi assert(pthread_kill(thread, SIGUSR1) == 0);
732b5b01cfSmpi assert(pthread_join(thread, NULL) == 0);
742b5b01cfSmpi
752b5b01cfSmpi /* Wait until another thread awakes us. */
762b5b01cfSmpi assert(pthread_create(&thread, NULL, awakener, NULL) == 0);
77116b6a5eSvisa assert(futex_twait(&lock, 0, 0, NULL, 0) == 0);
782b5b01cfSmpi assert(pthread_join(thread, NULL) == 0);
792b5b01cfSmpi
80116b6a5eSvisa /* Create a uvm object for sharing a lock. */
81116b6a5eSvisa fd = mkstemp(filename);
82116b6a5eSvisa assert(fd != -1);
83116b6a5eSvisa unlink(filename);
84116b6a5eSvisa assert(ftruncate(fd, 65536) == 0);
85116b6a5eSvisa shlock = mmap(NULL, sizeof(*shlock), PROT_READ | PROT_WRITE,
86116b6a5eSvisa MAP_SHARED, fd, 0);
87116b6a5eSvisa assert(shlock != MAP_FAILED);
88116b6a5eSvisa close(fd);
89116b6a5eSvisa
90116b6a5eSvisa /* Wake another process. */
91116b6a5eSvisa pid = fork();
92116b6a5eSvisa assert(pid != -1);
93116b6a5eSvisa if (pid == 0) {
94116b6a5eSvisa usleep(50000);
95116b6a5eSvisa futex_wake(shlock, -1, 0);
96116b6a5eSvisa _exit(0);
97116b6a5eSvisa } else {
98116b6a5eSvisa assert(futex_twait(shlock, 0, 0, NULL, 0) == 0);
99116b6a5eSvisa assert(waitpid(pid, &status, 0) == pid);
100116b6a5eSvisa assert(WIFEXITED(status));
101116b6a5eSvisa assert(WEXITSTATUS(status) == 0);
102116b6a5eSvisa }
103116b6a5eSvisa
104116b6a5eSvisa /* Cannot wake another process using a private futex. */
105116b6a5eSvisa for (i = 1; i < 4; i++) {
106116b6a5eSvisa pid = fork();
107116b6a5eSvisa assert(pid != -1);
108116b6a5eSvisa if (pid == 0) {
109116b6a5eSvisa usleep(50000);
110116b6a5eSvisa futex_wake(shlock, -1,
111116b6a5eSvisa (i & 1) ? FUTEX_PRIVATE_FLAG : 0);
112116b6a5eSvisa _exit(0);
113116b6a5eSvisa } else {
114116b6a5eSvisa tmo.tv_sec = 0;
115116b6a5eSvisa tmo.tv_nsec = 200000000;
116116b6a5eSvisa assert(futex_twait(shlock, 0, CLOCK_REALTIME, &tmo,
117f18e4490Skettenis (i & 2) ? FUTEX_PRIVATE_FLAG : 0) == -1);
118f18e4490Skettenis assert(errno == ETIMEDOUT);
119116b6a5eSvisa assert(waitpid(pid, &status, 0) == pid);
120116b6a5eSvisa assert(WIFEXITED(status));
121116b6a5eSvisa assert(WEXITSTATUS(status) == 0);
122116b6a5eSvisa }
123116b6a5eSvisa }
124116b6a5eSvisa
125116b6a5eSvisa assert(munmap(shlock, sizeof(*shlock)) == 0);
126116b6a5eSvisa
127*ebff625aSkettenis /* Create anonymous memory for sharing a lock. */
128*ebff625aSkettenis shlock = mmap(NULL, sizeof(*shlock), PROT_READ | PROT_WRITE,
129*ebff625aSkettenis MAP_ANON | MAP_SHARED, -1, 0);
130*ebff625aSkettenis assert(shlock != MAP_FAILED);
131*ebff625aSkettenis
132*ebff625aSkettenis /* Wake another process. */
133*ebff625aSkettenis pid = fork();
134*ebff625aSkettenis assert(pid != -1);
135*ebff625aSkettenis if (pid == 0) {
136*ebff625aSkettenis usleep(50000);
137*ebff625aSkettenis futex_wake(shlock, -1, 0);
138*ebff625aSkettenis _exit(0);
139*ebff625aSkettenis } else {
140*ebff625aSkettenis assert(futex_twait(shlock, 0, 0, NULL, 0) == 0);
141*ebff625aSkettenis assert(waitpid(pid, &status, 0) == pid);
142*ebff625aSkettenis assert(WIFEXITED(status));
143*ebff625aSkettenis assert(WEXITSTATUS(status) == 0);
144*ebff625aSkettenis }
145*ebff625aSkettenis
146*ebff625aSkettenis /* Cannot wake another process using a private futex. */
147*ebff625aSkettenis for (i = 1; i < 4; i++) {
148*ebff625aSkettenis pid = fork();
149*ebff625aSkettenis assert(pid != -1);
150*ebff625aSkettenis if (pid == 0) {
151*ebff625aSkettenis usleep(50000);
152*ebff625aSkettenis futex_wake(shlock, -1,
153*ebff625aSkettenis (i & 1) ? FUTEX_PRIVATE_FLAG : 0);
154*ebff625aSkettenis _exit(0);
155*ebff625aSkettenis } else {
156*ebff625aSkettenis tmo.tv_sec = 0;
157*ebff625aSkettenis tmo.tv_nsec = 200000000;
158*ebff625aSkettenis assert(futex_twait(shlock, 0, CLOCK_REALTIME, &tmo,
159*ebff625aSkettenis (i & 2) ? FUTEX_PRIVATE_FLAG : 0) == -1);
160*ebff625aSkettenis assert(errno == ETIMEDOUT);
161*ebff625aSkettenis assert(waitpid(pid, &status, 0) == pid);
162*ebff625aSkettenis assert(WIFEXITED(status));
163*ebff625aSkettenis assert(WEXITSTATUS(status) == 0);
164*ebff625aSkettenis }
165*ebff625aSkettenis }
166*ebff625aSkettenis
167*ebff625aSkettenis assert(munmap(shlock, sizeof(*shlock)) == 0);
168*ebff625aSkettenis
1692b5b01cfSmpi return 0;
1702b5b01cfSmpi }
1712b5b01cfSmpi
1722b5b01cfSmpi void
handler(int sig)1732b5b01cfSmpi handler(int sig)
1742b5b01cfSmpi {
1752b5b01cfSmpi }
1762b5b01cfSmpi
1772b5b01cfSmpi void *
signaled(void * arg)1782b5b01cfSmpi signaled(void *arg)
1792b5b01cfSmpi {
1802b5b01cfSmpi /* Wait until receiving a signal. */
181f18e4490Skettenis assert(futex_twait(&lock, 0, 0, NULL, 0) == -1);
182f18e4490Skettenis assert(errno == EINTR);
183116b6a5eSvisa return NULL;
1842b5b01cfSmpi }
1852b5b01cfSmpi
1862b5b01cfSmpi void *
awakener(void * arg)1872b5b01cfSmpi awakener(void *arg)
1882b5b01cfSmpi {
1892b5b01cfSmpi usleep(100);
190116b6a5eSvisa assert(futex_wake(&lock, -1, 0) == 1);
191116b6a5eSvisa return NULL;
1922b5b01cfSmpi }
193