Hello,
When I do mmap(MAP_ANONYMOUS | MAP_SHARED) and then fork(), it seems
that futex(2) wakeups are not delivered between child and parent in
that memory. It does work as expected if I instead use
shmget(IPC_PRIVATE).
Below is a standalone test program. I tested it with the four OSes
mentioned, and the two shmem types depending on that #if, and all
worked as expected except the OpenBSD/mmap case, which hangs.
Is it a bug?
$ uname -a
OpenBSD openbsd6.localdomain 6.9 GENERIC.MP#473 amd64
Thanks,
=== 8< ===
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(__linux__)
#include
#include
#elif defined(__OpenBSD__)
#include
#include
#elif defined(__FreeBSD__)
#include
#include
#elif defined(__APPLE__)
#define UL_COMPARE_AND_WAIT_SHARED 3
#define ULF_WAKE_ALL 0x0100
extern int __ulock_wait(uint32_t operation, void *addr, uint64_t
value, uint32_t timeout);
extern int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
#endif
static int
my_futex_wait_u32(void *fut, uint32_t value, struct timespec *timeout)
{
#if defined(__linux__)
if (syscall(SYS_futex, fut, FUTEX_WAIT, value, timeout, 0, 0) == 0)
return 0;
#elif defined(__OpenBSD__)
if ((errno = futex(fut, FUTEX_WAIT, (int) value, timeout, NULL)) == 0)
return 0;
if (errno == ECANCELED)
errno = EINTR;
#elif defined(__FreeBSD__)
if (_umtx_op(fut, UMTX_OP_WAIT_UINT, value, 0, timeout) == 0)
return 0;
#elif defined (__APPLE__)
if (__ulock_wait(UL_COMPARE_AND_WAIT_SHARED, (void *) fut,
value, timeout ? timeout->tv_sec * 100 + timeout->tv_nsec / 1000 :
0) >
= 0)
return 0;
#else
errno = ENOSYS;
#endif
return -1;
}
static int
my_futex_wake(void *fut, int nwaiters)
{
#if defined(__linux__)
if (syscall(SYS_futex, fut, FUTEX_WAKE, nwaiters, NULL, 0, 0) >= 0)
return 0;
#elif defined(__OpenBSD__)
if (futex(fut, FUTEX_WAKE, nwaiters, NULL, NULL) >= 0)
return 0;
#elif defined(__FreeBSD__)
if (_umtx_op(fut, UMTX_OP_WAKE, nwaiters, 0, 0) == 0)
return 0;
#elif defined (__APPLE__)
if (__ulock_wake(UL_COMPARE_AND_WAIT_SHARED | (nwaiters > 1 ?
ULF_WAKE_ALL : 0), (void *) fut, 0) >= 0)
return 0;
if (errno == ENOENT)
return 0;
#else
errno = ENOSYS;
#endif
return -1;
}
int
main(int argc, char *argv[])
{
pid_t pid;
uint32_t *memory;
int status;
#if 1
memory = mmap(NULL, sizeof(uint32_t), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (memory == MAP_FAILED) {
perror("mmap");
return EXIT_FAILURE;
}
#else
int shm_id;
shm_id = shmget(IPC_PRIVATE, sizeof(uint32_t), IPC_CREAT | 0666);
if (shm_id < 0) {
perror("shmget");
return EXIT_FAILURE;
}
memory = shmat(shm_id, NULL, 0);
if ((intptr_t) memory == -1) {
perror("shmat");
return EXIT_FAILURE;
}
#endif
*memory = 42;
pid = fork();
if (pid == -1) {
perror("fork");
return EXIT_FAILURE;
} else if (pid > 0) {
printf("hello from parent, will wait for futex...\n");
if (my_futex_wait_u32(memory, 42, NULL) < 0) {
perror("futex_wait");
wait(&status);
return EXIT_FAILURE;
}
wait(&status);
return EXIT_SUCCESS;
} else {
printf("hello from child, will wake futex...\n");
sleep(1);
if (my_futex_wake(memory, INT_MAX) < 0) {
perror("futex_wake");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
}