-/* $OpenBSD: futex.c,v 1.2 2017/04/30 10:11:03 mpi Exp $ */
+/* $OpenBSD: futex.c,v 1.3 2018/08/26 06:50:30 visa Exp $ */
/*
* Copyright (c) 2017 Martin Pieuchot
*
#include <sys/time.h>
#include <sys/futex.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "futex.h"
uint32_t lock = 0;
+uint32_t *shlock;
void handler(int);
void *signaled(void *);
int
main(int argc, char *argv[])
{
+ char filename[] = "/tmp/futex.XXXXXXXX";
struct sigaction sa;
- struct timespec abs = { 0, 5000 };
+ struct timespec tmo = { 0, 5000 };
pthread_t thread;
+ pid_t pid;
+ int fd, i, status;
/* Invalid operation */
assert(futex(&lock, 0xFFFF, 0, 0, NULL) == ENOSYS);
/* Incorrect pointer */
- assert(futex_twait((void *)0xdeadbeef, 1, 0, NULL) == EFAULT);
+ assert(futex_twait((void *)0xdeadbeef, 1, 0, NULL, 0) == EFAULT);
/* If (lock != 1) return EAGAIN */
- assert(futex_twait(&lock, 1, 0, NULL) == EAGAIN);
+ assert(futex_twait(&lock, 1, 0, NULL, 0) == EAGAIN);
/* Deadlock for 5000ns */
- assert(futex_twait(&lock, 0, CLOCK_REALTIME, &abs) == ETIMEDOUT);
+ assert(futex_twait(&lock, 0, CLOCK_REALTIME, &tmo, 0) == ETIMEDOUT);
/* Interrupt a thread waiting on a futex. */
memset(&sa, 0, sizeof(sa));
/* Wait until another thread awakes us. */
assert(pthread_create(&thread, NULL, awakener, NULL) == 0);
- assert(futex_twait(&lock, 0, 0, NULL) == 0);
+ assert(futex_twait(&lock, 0, 0, NULL, 0) == 0);
assert(pthread_join(thread, NULL) == 0);
+ /* Create a uvm object for sharing a lock. */
+ fd = mkstemp(filename);
+ assert(fd != -1);
+ unlink(filename);
+ assert(ftruncate(fd, 65536) == 0);
+ shlock = mmap(NULL, sizeof(*shlock), PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ assert(shlock != MAP_FAILED);
+ close(fd);
+
+ /* Wake another process. */
+ pid = fork();
+ assert(pid != -1);
+ if (pid == 0) {
+ usleep(50000);
+ futex_wake(shlock, -1, 0);
+ _exit(0);
+ } else {
+ assert(futex_twait(shlock, 0, 0, NULL, 0) == 0);
+ assert(waitpid(pid, &status, 0) == pid);
+ assert(WIFEXITED(status));
+ assert(WEXITSTATUS(status) == 0);
+ }
+
+ /* Cannot wake another process using a private futex. */
+ for (i = 1; i < 4; i++) {
+ pid = fork();
+ assert(pid != -1);
+ if (pid == 0) {
+ usleep(50000);
+ futex_wake(shlock, -1,
+ (i & 1) ? FUTEX_PRIVATE_FLAG : 0);
+ _exit(0);
+ } else {
+ tmo.tv_sec = 0;
+ tmo.tv_nsec = 200000000;
+ assert(futex_twait(shlock, 0, CLOCK_REALTIME, &tmo,
+ (i & 2) ? FUTEX_PRIVATE_FLAG : 0) == ETIMEDOUT);
+ assert(waitpid(pid, &status, 0) == pid);
+ assert(WIFEXITED(status));
+ assert(WEXITSTATUS(status) == 0);
+ }
+ }
+
+ assert(munmap(shlock, sizeof(*shlock)) == 0);
+
return 0;
}
signaled(void *arg)
{
/* Wait until receiving a signal. */
- assert(futex_twait(&lock, 0, 0, NULL) == EINTR);
+ assert(futex_twait(&lock, 0, 0, NULL, 0) == EINTR);
+ return NULL;
}
void *
awakener(void *arg)
{
usleep(100);
- assert(futex_wake(&lock, -1) == 1);
+ assert(futex_wake(&lock, -1, 0) == 1);
+ return NULL;
}
-/* $OpenBSD: futex.h,v 1.1 2017/04/30 09:03:58 mpi Exp $ */
+/* $OpenBSD: futex.h,v 1.2 2018/08/26 06:50:30 visa Exp $ */
/*
* Copyright (c) 2017 Martin Pieuchot
*
*/
static inline int
-futex_wake(volatile uint32_t *p, int n)
+futex_wake(volatile uint32_t *p, int n, int priv)
{
- return futex(p, FUTEX_WAKE, n, NULL, NULL);
+ return futex(p, FUTEX_WAKE | priv, n, NULL, NULL);
}
static inline void
-futex_wait(volatile uint32_t *p, int val)
+futex_wait(volatile uint32_t *p, int val, int priv)
{
while (*p != (uint32_t)val)
- futex(p, FUTEX_WAIT, val, NULL, NULL);
+ futex(p, FUTEX_WAIT | priv, val, NULL, NULL);
}
static inline int
futex_twait(volatile uint32_t *p, int val, clockid_t clockid,
- const struct timespec *abs)
+ const struct timespec *timeout, int priv)
{
- struct timespec rel;
-
- if (abs == NULL)
- return futex(p, FUTEX_WAIT, val, NULL, NULL);
-
- if (abs->tv_nsec >= 1000000000 || clock_gettime(clockid, &rel))
- return (EINVAL);
-
- rel.tv_sec = abs->tv_sec - rel.tv_sec;
- if ((rel.tv_nsec = abs->tv_nsec - rel.tv_nsec) < 0) {
- rel.tv_sec--;
- rel.tv_nsec += 1000000000;
- }
- if (rel.tv_sec < 0)
- return (ETIMEDOUT);
-
- return futex(p, FUTEX_WAIT, val, &rel, NULL);
+ return futex(p, FUTEX_WAIT | priv, val, timeout, NULL);
}
static inline int