mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

Test a more realistic usage pattern, and one with heavy contention, in order to actually exercise ntsync's internal synchronization. This test has several threads in a tight loop acquiring a mutex, modifying some shared data, and then releasing the mutex. At the end we check if the data is consistent. Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com> Link: https://lore.kernel.org/r/20241213193511.457338-28-zfigura@codeweavers.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1343 lines
31 KiB
C
1343 lines
31 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Various unit tests for the "ntsync" synchronization primitive driver.
|
|
*
|
|
* Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <pthread.h>
|
|
#include <linux/ntsync.h>
|
|
#include "../../kselftest_harness.h"
|
|
|
|
static int read_sem_state(int sem, __u32 *count, __u32 *max)
|
|
{
|
|
struct ntsync_sem_args args;
|
|
int ret;
|
|
|
|
memset(&args, 0xcc, sizeof(args));
|
|
ret = ioctl(sem, NTSYNC_IOC_SEM_READ, &args);
|
|
*count = args.count;
|
|
*max = args.max;
|
|
return ret;
|
|
}
|
|
|
|
#define check_sem_state(sem, count, max) \
|
|
({ \
|
|
__u32 __count, __max; \
|
|
int ret = read_sem_state((sem), &__count, &__max); \
|
|
EXPECT_EQ(0, ret); \
|
|
EXPECT_EQ((count), __count); \
|
|
EXPECT_EQ((max), __max); \
|
|
})
|
|
|
|
static int release_sem(int sem, __u32 *count)
|
|
{
|
|
return ioctl(sem, NTSYNC_IOC_SEM_RELEASE, count);
|
|
}
|
|
|
|
static int read_mutex_state(int mutex, __u32 *count, __u32 *owner)
|
|
{
|
|
struct ntsync_mutex_args args;
|
|
int ret;
|
|
|
|
memset(&args, 0xcc, sizeof(args));
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &args);
|
|
*count = args.count;
|
|
*owner = args.owner;
|
|
return ret;
|
|
}
|
|
|
|
#define check_mutex_state(mutex, count, owner) \
|
|
({ \
|
|
__u32 __count, __owner; \
|
|
int ret = read_mutex_state((mutex), &__count, &__owner); \
|
|
EXPECT_EQ(0, ret); \
|
|
EXPECT_EQ((count), __count); \
|
|
EXPECT_EQ((owner), __owner); \
|
|
})
|
|
|
|
static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
|
|
{
|
|
struct ntsync_mutex_args args;
|
|
int ret;
|
|
|
|
args.owner = owner;
|
|
args.count = 0xdeadbeef;
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_UNLOCK, &args);
|
|
*count = args.count;
|
|
return ret;
|
|
}
|
|
|
|
static int read_event_state(int event, __u32 *signaled, __u32 *manual)
|
|
{
|
|
struct ntsync_event_args args;
|
|
int ret;
|
|
|
|
memset(&args, 0xcc, sizeof(args));
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_READ, &args);
|
|
*signaled = args.signaled;
|
|
*manual = args.manual;
|
|
return ret;
|
|
}
|
|
|
|
#define check_event_state(event, signaled, manual) \
|
|
({ \
|
|
__u32 __signaled, __manual; \
|
|
int ret = read_event_state((event), &__signaled, &__manual); \
|
|
EXPECT_EQ(0, ret); \
|
|
EXPECT_EQ((signaled), __signaled); \
|
|
EXPECT_EQ((manual), __manual); \
|
|
})
|
|
|
|
static int wait_objs(int fd, unsigned long request, __u32 count,
|
|
const int *objs, __u32 owner, int alert, __u32 *index)
|
|
{
|
|
struct ntsync_wait_args args = {0};
|
|
struct timespec timeout;
|
|
int ret;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
|
|
args.timeout = timeout.tv_sec * 1000000000 + timeout.tv_nsec;
|
|
args.count = count;
|
|
args.objs = (uintptr_t)objs;
|
|
args.owner = owner;
|
|
args.index = 0xdeadbeef;
|
|
args.alert = alert;
|
|
ret = ioctl(fd, request, &args);
|
|
*index = args.index;
|
|
return ret;
|
|
}
|
|
|
|
static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
{
|
|
return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, 0, index);
|
|
}
|
|
|
|
static int wait_all(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
|
|
{
|
|
return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, 0, index);
|
|
}
|
|
|
|
static int wait_any_alert(int fd, __u32 count, const int *objs,
|
|
__u32 owner, int alert, __u32 *index)
|
|
{
|
|
return wait_objs(fd, NTSYNC_IOC_WAIT_ANY,
|
|
count, objs, owner, alert, index);
|
|
}
|
|
|
|
static int wait_all_alert(int fd, __u32 count, const int *objs,
|
|
__u32 owner, int alert, __u32 *index)
|
|
{
|
|
return wait_objs(fd, NTSYNC_IOC_WAIT_ALL,
|
|
count, objs, owner, alert, index);
|
|
}
|
|
|
|
TEST(semaphore_state)
|
|
{
|
|
struct ntsync_sem_args sem_args;
|
|
struct timespec timeout;
|
|
__u32 count, index;
|
|
int fd, ret, sem;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
sem_args.count = 3;
|
|
sem_args.max = 2;
|
|
sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_EQ(-1, sem);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
sem_args.count = 2;
|
|
sem_args.max = 2;
|
|
sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, sem);
|
|
check_sem_state(sem, 2, 2);
|
|
|
|
count = 0;
|
|
ret = release_sem(sem, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, count);
|
|
check_sem_state(sem, 2, 2);
|
|
|
|
count = 1;
|
|
ret = release_sem(sem, &count);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOVERFLOW, errno);
|
|
check_sem_state(sem, 2, 2);
|
|
|
|
ret = wait_any(fd, 1, &sem, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(sem, 1, 2);
|
|
|
|
ret = wait_any(fd, 1, &sem, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(sem, 0, 2);
|
|
|
|
ret = wait_any(fd, 1, &sem, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
count = 3;
|
|
ret = release_sem(sem, &count);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOVERFLOW, errno);
|
|
check_sem_state(sem, 0, 2);
|
|
|
|
count = 2;
|
|
ret = release_sem(sem, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
check_sem_state(sem, 2, 2);
|
|
|
|
ret = wait_any(fd, 1, &sem, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
ret = wait_any(fd, 1, &sem, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
count = 1;
|
|
ret = release_sem(sem, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
check_sem_state(sem, 1, 2);
|
|
|
|
count = ~0u;
|
|
ret = release_sem(sem, &count);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOVERFLOW, errno);
|
|
check_sem_state(sem, 1, 2);
|
|
|
|
close(sem);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(mutex_state)
|
|
{
|
|
struct ntsync_mutex_args mutex_args;
|
|
__u32 owner, count, index;
|
|
struct timespec timeout;
|
|
int fd, ret, mutex;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
mutex_args.owner = 123;
|
|
mutex_args.count = 0;
|
|
mutex = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_EQ(-1, mutex);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
mutex_args.owner = 0;
|
|
mutex_args.count = 2;
|
|
mutex = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_EQ(-1, mutex);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
mutex_args.owner = 123;
|
|
mutex_args.count = 2;
|
|
mutex = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, mutex);
|
|
check_mutex_state(mutex, 2, 123);
|
|
|
|
ret = unlock_mutex(mutex, 0, &count);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
ret = unlock_mutex(mutex, 456, &count);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EPERM, errno);
|
|
check_mutex_state(mutex, 2, 123);
|
|
|
|
ret = unlock_mutex(mutex, 123, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, count);
|
|
check_mutex_state(mutex, 1, 123);
|
|
|
|
ret = unlock_mutex(mutex, 123, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, count);
|
|
check_mutex_state(mutex, 0, 0);
|
|
|
|
ret = unlock_mutex(mutex, 123, &count);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EPERM, errno);
|
|
|
|
ret = wait_any(fd, 1, &mutex, 456, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_mutex_state(mutex, 1, 456);
|
|
|
|
ret = wait_any(fd, 1, &mutex, 456, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_mutex_state(mutex, 2, 456);
|
|
|
|
ret = unlock_mutex(mutex, 456, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, count);
|
|
check_mutex_state(mutex, 1, 456);
|
|
|
|
ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
owner = 0;
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
owner = 123;
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EPERM, errno);
|
|
check_mutex_state(mutex, 1, 456);
|
|
|
|
owner = 456;
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOWNERDEAD, errno);
|
|
EXPECT_EQ(0, mutex_args.count);
|
|
EXPECT_EQ(0, mutex_args.owner);
|
|
|
|
memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOWNERDEAD, errno);
|
|
EXPECT_EQ(0, mutex_args.count);
|
|
EXPECT_EQ(0, mutex_args.owner);
|
|
|
|
ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOWNERDEAD, errno);
|
|
EXPECT_EQ(0, index);
|
|
check_mutex_state(mutex, 1, 123);
|
|
|
|
owner = 123;
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
memset(&mutex_args, 0xcc, sizeof(mutex_args));
|
|
ret = ioctl(mutex, NTSYNC_IOC_MUTEX_READ, &mutex_args);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOWNERDEAD, errno);
|
|
EXPECT_EQ(0, mutex_args.count);
|
|
EXPECT_EQ(0, mutex_args.owner);
|
|
|
|
ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOWNERDEAD, errno);
|
|
EXPECT_EQ(0, index);
|
|
check_mutex_state(mutex, 1, 123);
|
|
|
|
close(mutex);
|
|
|
|
mutex_args.owner = 0;
|
|
mutex_args.count = 0;
|
|
mutex = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, mutex);
|
|
check_mutex_state(mutex, 0, 0);
|
|
|
|
ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_mutex_state(mutex, 1, 123);
|
|
|
|
close(mutex);
|
|
|
|
mutex_args.owner = 123;
|
|
mutex_args.count = ~0u;
|
|
mutex = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, mutex);
|
|
check_mutex_state(mutex, ~0u, 123);
|
|
|
|
ret = wait_any(fd, 1, &mutex, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
close(mutex);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(manual_event_state)
|
|
{
|
|
struct ntsync_event_args event_args;
|
|
__u32 index, signaled;
|
|
int fd, event, ret;
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
event_args.manual = 1;
|
|
event_args.signaled = 0;
|
|
event = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, event);
|
|
check_event_state(event, 0, 1);
|
|
|
|
signaled = 0xdeadbeef;
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(event, 1, 1);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
check_event_state(event, 1, 1);
|
|
|
|
ret = wait_any(fd, 1, &event, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_event_state(event, 1, 1);
|
|
|
|
signaled = 0xdeadbeef;
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
check_event_state(event, 0, 1);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(event, 0, 1);
|
|
|
|
ret = wait_any(fd, 1, &event, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
check_event_state(event, 0, 1);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(event, 0, 1);
|
|
|
|
close(event);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(auto_event_state)
|
|
{
|
|
struct ntsync_event_args event_args;
|
|
__u32 index, signaled;
|
|
int fd, event, ret;
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
event_args.manual = 0;
|
|
event_args.signaled = 1;
|
|
event = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, event);
|
|
|
|
check_event_state(event, 1, 0);
|
|
|
|
signaled = 0xdeadbeef;
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
check_event_state(event, 1, 0);
|
|
|
|
ret = wait_any(fd, 1, &event, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_event_state(event, 0, 0);
|
|
|
|
signaled = 0xdeadbeef;
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(event, 0, 0);
|
|
|
|
ret = wait_any(fd, 1, &event, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
check_event_state(event, 0, 0);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(event, 0, 0);
|
|
|
|
close(event);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(test_wait_any)
|
|
{
|
|
int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
|
|
struct ntsync_mutex_args mutex_args = {0};
|
|
struct ntsync_sem_args sem_args = {0};
|
|
__u32 owner, index, count, i;
|
|
struct timespec timeout;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
sem_args.count = 2;
|
|
sem_args.max = 3;
|
|
objs[0] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[0]);
|
|
|
|
mutex_args.owner = 0;
|
|
mutex_args.count = 0;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
ret = wait_any(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 1, 3);
|
|
check_mutex_state(objs[1], 0, 0);
|
|
|
|
ret = wait_any(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 0, 3);
|
|
check_mutex_state(objs[1], 0, 0);
|
|
|
|
ret = wait_any(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, index);
|
|
check_sem_state(objs[0], 0, 3);
|
|
check_mutex_state(objs[1], 1, 123);
|
|
|
|
count = 1;
|
|
ret = release_sem(objs[0], &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
|
|
ret = wait_any(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 0, 3);
|
|
check_mutex_state(objs[1], 1, 123);
|
|
|
|
ret = wait_any(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, index);
|
|
check_sem_state(objs[0], 0, 3);
|
|
check_mutex_state(objs[1], 2, 123);
|
|
|
|
ret = wait_any(fd, 2, objs, 456, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
owner = 123;
|
|
ret = ioctl(objs[1], NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_any(fd, 2, objs, 456, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOWNERDEAD, errno);
|
|
EXPECT_EQ(1, index);
|
|
|
|
ret = wait_any(fd, 2, objs, 456, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, index);
|
|
|
|
close(objs[1]);
|
|
|
|
/* test waiting on the same object twice */
|
|
|
|
count = 2;
|
|
ret = release_sem(objs[0], &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
|
|
objs[1] = objs[0];
|
|
ret = wait_any(fd, 2, objs, 456, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 1, 3);
|
|
|
|
ret = wait_any(fd, 0, NULL, 456, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
for (i = 1; i < NTSYNC_MAX_WAIT_COUNT + 1; ++i)
|
|
objs[i] = objs[0];
|
|
|
|
ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
|
|
ret = wait_any(fd, NTSYNC_MAX_WAIT_COUNT + 1, objs, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
ret = wait_any(fd, -1, objs, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
close(objs[0]);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(test_wait_all)
|
|
{
|
|
struct ntsync_event_args event_args = {0};
|
|
struct ntsync_mutex_args mutex_args = {0};
|
|
struct ntsync_sem_args sem_args = {0};
|
|
__u32 owner, index, count;
|
|
int objs[2], fd, ret;
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
sem_args.count = 2;
|
|
sem_args.max = 3;
|
|
objs[0] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[0]);
|
|
|
|
mutex_args.owner = 0;
|
|
mutex_args.count = 0;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
ret = wait_all(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 1, 3);
|
|
check_mutex_state(objs[1], 1, 123);
|
|
|
|
ret = wait_all(fd, 2, objs, 456, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
check_sem_state(objs[0], 1, 3);
|
|
check_mutex_state(objs[1], 1, 123);
|
|
|
|
ret = wait_all(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 0, 3);
|
|
check_mutex_state(objs[1], 2, 123);
|
|
|
|
ret = wait_all(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
check_sem_state(objs[0], 0, 3);
|
|
check_mutex_state(objs[1], 2, 123);
|
|
|
|
count = 3;
|
|
ret = release_sem(objs[0], &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
|
|
ret = wait_all(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 2, 3);
|
|
check_mutex_state(objs[1], 3, 123);
|
|
|
|
owner = 123;
|
|
ret = ioctl(objs[1], NTSYNC_IOC_MUTEX_KILL, &owner);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_all(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EOWNERDEAD, errno);
|
|
check_sem_state(objs[0], 1, 3);
|
|
check_mutex_state(objs[1], 1, 123);
|
|
|
|
close(objs[1]);
|
|
|
|
event_args.manual = true;
|
|
event_args.signaled = true;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
ret = wait_all(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
check_sem_state(objs[0], 0, 3);
|
|
check_event_state(objs[1], 1, 1);
|
|
|
|
close(objs[1]);
|
|
|
|
/* test waiting on the same object twice */
|
|
objs[1] = objs[0];
|
|
ret = wait_all(fd, 2, objs, 123, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(EINVAL, errno);
|
|
|
|
close(objs[0]);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
struct wake_args {
|
|
int fd;
|
|
int obj;
|
|
};
|
|
|
|
struct wait_args {
|
|
int fd;
|
|
unsigned long request;
|
|
struct ntsync_wait_args *args;
|
|
int ret;
|
|
int err;
|
|
};
|
|
|
|
static void *wait_thread(void *arg)
|
|
{
|
|
struct wait_args *args = arg;
|
|
|
|
args->ret = ioctl(args->fd, args->request, args->args);
|
|
args->err = errno;
|
|
return NULL;
|
|
}
|
|
|
|
static __u64 get_abs_timeout(unsigned int ms)
|
|
{
|
|
struct timespec timeout;
|
|
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
|
return (timeout.tv_sec * 1000000000) + timeout.tv_nsec + (ms * 1000000);
|
|
}
|
|
|
|
static int wait_for_thread(pthread_t thread, unsigned int ms)
|
|
{
|
|
struct timespec timeout;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &timeout);
|
|
timeout.tv_nsec += ms * 1000000;
|
|
timeout.tv_sec += (timeout.tv_nsec / 1000000000);
|
|
timeout.tv_nsec %= 1000000000;
|
|
return pthread_timedjoin_np(thread, NULL, &timeout);
|
|
}
|
|
|
|
TEST(wake_any)
|
|
{
|
|
struct ntsync_event_args event_args = {0};
|
|
struct ntsync_mutex_args mutex_args = {0};
|
|
struct ntsync_wait_args wait_args = {0};
|
|
struct ntsync_sem_args sem_args = {0};
|
|
struct wait_args thread_args;
|
|
__u32 count, index, signaled;
|
|
int objs[2], fd, ret;
|
|
pthread_t thread;
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
sem_args.count = 0;
|
|
sem_args.max = 3;
|
|
objs[0] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[0]);
|
|
|
|
mutex_args.owner = 123;
|
|
mutex_args.count = 1;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
/* test waking the semaphore */
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
wait_args.objs = (uintptr_t)objs;
|
|
wait_args.count = 2;
|
|
wait_args.owner = 456;
|
|
wait_args.index = 0xdeadbeef;
|
|
thread_args.fd = fd;
|
|
thread_args.args = &wait_args;
|
|
thread_args.request = NTSYNC_IOC_WAIT_ANY;
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
count = 1;
|
|
ret = release_sem(objs[0], &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
check_sem_state(objs[0], 0, 3);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(0, wait_args.index);
|
|
|
|
/* test waking the mutex */
|
|
|
|
/* first grab it again for owner 123 */
|
|
ret = wait_any(fd, 1, &objs[1], 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
wait_args.owner = 456;
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
ret = unlock_mutex(objs[1], 123, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, count);
|
|
|
|
ret = pthread_tryjoin_np(thread, NULL);
|
|
EXPECT_EQ(EBUSY, ret);
|
|
|
|
ret = unlock_mutex(objs[1], 123, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, mutex_args.count);
|
|
check_mutex_state(objs[1], 1, 456);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(1, wait_args.index);
|
|
|
|
close(objs[1]);
|
|
|
|
/* test waking events */
|
|
|
|
event_args.manual = false;
|
|
event_args.signaled = false;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
ret = ioctl(objs[1], NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(objs[1], 0, 0);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(1, wait_args.index);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
ret = ioctl(objs[1], NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(objs[1], 0, 0);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(1, wait_args.index);
|
|
|
|
close(objs[1]);
|
|
|
|
event_args.manual = true;
|
|
event_args.signaled = false;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
ret = ioctl(objs[1], NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(objs[1], 1, 1);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(1, wait_args.index);
|
|
|
|
ret = ioctl(objs[1], NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
ret = ioctl(objs[1], NTSYNC_IOC_EVENT_PULSE, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
check_event_state(objs[1], 0, 1);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(1, wait_args.index);
|
|
|
|
/* delete an object while it's being waited on */
|
|
|
|
wait_args.timeout = get_abs_timeout(200);
|
|
wait_args.owner = 123;
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
close(objs[0]);
|
|
close(objs[1]);
|
|
|
|
ret = wait_for_thread(thread, 200);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(-1, thread_args.ret);
|
|
EXPECT_EQ(ETIMEDOUT, thread_args.err);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(wake_all)
|
|
{
|
|
struct ntsync_event_args manual_event_args = {0};
|
|
struct ntsync_event_args auto_event_args = {0};
|
|
struct ntsync_mutex_args mutex_args = {0};
|
|
struct ntsync_wait_args wait_args = {0};
|
|
struct ntsync_sem_args sem_args = {0};
|
|
struct wait_args thread_args;
|
|
__u32 count, index, signaled;
|
|
int objs[4], fd, ret;
|
|
pthread_t thread;
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
sem_args.count = 0;
|
|
sem_args.max = 3;
|
|
objs[0] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[0]);
|
|
|
|
mutex_args.owner = 123;
|
|
mutex_args.count = 1;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
manual_event_args.manual = true;
|
|
manual_event_args.signaled = true;
|
|
objs[2] = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &manual_event_args);
|
|
EXPECT_LE(0, objs[2]);
|
|
|
|
auto_event_args.manual = false;
|
|
auto_event_args.signaled = true;
|
|
objs[3] = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &auto_event_args);
|
|
EXPECT_EQ(0, objs[3]);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
wait_args.objs = (uintptr_t)objs;
|
|
wait_args.count = 4;
|
|
wait_args.owner = 456;
|
|
thread_args.fd = fd;
|
|
thread_args.args = &wait_args;
|
|
thread_args.request = NTSYNC_IOC_WAIT_ALL;
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
count = 1;
|
|
ret = release_sem(objs[0], &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
|
|
ret = pthread_tryjoin_np(thread, NULL);
|
|
EXPECT_EQ(EBUSY, ret);
|
|
|
|
check_sem_state(objs[0], 1, 3);
|
|
|
|
ret = wait_any(fd, 1, &objs[0], 123, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
|
|
ret = unlock_mutex(objs[1], 123, &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, count);
|
|
|
|
ret = pthread_tryjoin_np(thread, NULL);
|
|
EXPECT_EQ(EBUSY, ret);
|
|
|
|
check_mutex_state(objs[1], 0, 0);
|
|
|
|
ret = ioctl(objs[2], NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
|
|
count = 2;
|
|
ret = release_sem(objs[0], &count);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, count);
|
|
check_sem_state(objs[0], 2, 3);
|
|
|
|
ret = ioctl(objs[3], NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, signaled);
|
|
|
|
ret = ioctl(objs[2], NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
|
|
ret = ioctl(objs[3], NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, signaled);
|
|
|
|
check_sem_state(objs[0], 1, 3);
|
|
check_mutex_state(objs[1], 1, 456);
|
|
check_event_state(objs[2], 1, 1);
|
|
check_event_state(objs[3], 0, 0);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
|
|
/* delete an object while it's being waited on */
|
|
|
|
wait_args.timeout = get_abs_timeout(200);
|
|
wait_args.owner = 123;
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
close(objs[0]);
|
|
close(objs[1]);
|
|
close(objs[2]);
|
|
close(objs[3]);
|
|
|
|
ret = wait_for_thread(thread, 200);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(-1, thread_args.ret);
|
|
EXPECT_EQ(ETIMEDOUT, thread_args.err);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(alert_any)
|
|
{
|
|
struct ntsync_event_args event_args = {0};
|
|
struct ntsync_wait_args wait_args = {0};
|
|
struct ntsync_sem_args sem_args = {0};
|
|
__u32 index, count, signaled;
|
|
struct wait_args thread_args;
|
|
int objs[2], event, fd, ret;
|
|
pthread_t thread;
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
sem_args.count = 0;
|
|
sem_args.max = 2;
|
|
objs[0] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[0]);
|
|
|
|
sem_args.count = 1;
|
|
sem_args.max = 2;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
event_args.manual = true;
|
|
event_args.signaled = true;
|
|
event = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, event);
|
|
|
|
ret = wait_any_alert(fd, 0, NULL, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_any_alert(fd, 0, NULL, 123, event, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_any_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(1, index);
|
|
|
|
ret = wait_any_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, index);
|
|
|
|
/* test wakeup via alert */
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
wait_args.objs = (uintptr_t)objs;
|
|
wait_args.count = 2;
|
|
wait_args.owner = 123;
|
|
wait_args.index = 0xdeadbeef;
|
|
wait_args.alert = event;
|
|
thread_args.fd = fd;
|
|
thread_args.args = &wait_args;
|
|
thread_args.request = NTSYNC_IOC_WAIT_ANY;
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(2, wait_args.index);
|
|
|
|
close(event);
|
|
|
|
/* test with an auto-reset event */
|
|
|
|
event_args.manual = false;
|
|
event_args.signaled = true;
|
|
event = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, event);
|
|
|
|
count = 1;
|
|
ret = release_sem(objs[0], &count);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_any_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
|
|
ret = wait_any_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, index);
|
|
|
|
ret = wait_any_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
close(event);
|
|
|
|
close(objs[0]);
|
|
close(objs[1]);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
TEST(alert_all)
|
|
{
|
|
struct ntsync_event_args event_args = {0};
|
|
struct ntsync_wait_args wait_args = {0};
|
|
struct ntsync_sem_args sem_args = {0};
|
|
struct wait_args thread_args;
|
|
__u32 index, count, signaled;
|
|
int objs[2], event, fd, ret;
|
|
pthread_t thread;
|
|
|
|
fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, fd);
|
|
|
|
sem_args.count = 2;
|
|
sem_args.max = 2;
|
|
objs[0] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[0]);
|
|
|
|
sem_args.count = 1;
|
|
sem_args.max = 2;
|
|
objs[1] = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
|
|
EXPECT_LE(0, objs[1]);
|
|
|
|
event_args.manual = true;
|
|
event_args.signaled = true;
|
|
event = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, event);
|
|
|
|
ret = wait_all_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
|
|
ret = wait_all_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, index);
|
|
|
|
/* test wakeup via alert */
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
wait_args.timeout = get_abs_timeout(1000);
|
|
wait_args.objs = (uintptr_t)objs;
|
|
wait_args.count = 2;
|
|
wait_args.owner = 123;
|
|
wait_args.index = 0xdeadbeef;
|
|
wait_args.alert = event;
|
|
thread_args.fd = fd;
|
|
thread_args.args = &wait_args;
|
|
thread_args.request = NTSYNC_IOC_WAIT_ALL;
|
|
ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(ETIMEDOUT, ret);
|
|
|
|
ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_for_thread(thread, 100);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, thread_args.ret);
|
|
EXPECT_EQ(2, wait_args.index);
|
|
|
|
close(event);
|
|
|
|
/* test with an auto-reset event */
|
|
|
|
event_args.manual = false;
|
|
event_args.signaled = true;
|
|
event = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, event);
|
|
|
|
count = 2;
|
|
ret = release_sem(objs[1], &count);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
ret = wait_all_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(0, index);
|
|
|
|
ret = wait_all_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(0, ret);
|
|
EXPECT_EQ(2, index);
|
|
|
|
ret = wait_all_alert(fd, 2, objs, 123, event, &index);
|
|
EXPECT_EQ(-1, ret);
|
|
EXPECT_EQ(ETIMEDOUT, errno);
|
|
|
|
close(event);
|
|
|
|
close(objs[0]);
|
|
close(objs[1]);
|
|
|
|
close(fd);
|
|
}
|
|
|
|
#define STRESS_LOOPS 10000
|
|
#define STRESS_THREADS 4
|
|
|
|
static unsigned int stress_counter;
|
|
static int stress_device, stress_start_event, stress_mutex;
|
|
|
|
static void *stress_thread(void *arg)
|
|
{
|
|
struct ntsync_wait_args wait_args = {0};
|
|
__u32 index, count, i;
|
|
int ret;
|
|
|
|
wait_args.timeout = UINT64_MAX;
|
|
wait_args.count = 1;
|
|
wait_args.objs = (uintptr_t)&stress_start_event;
|
|
wait_args.owner = gettid();
|
|
wait_args.index = 0xdeadbeef;
|
|
|
|
ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
|
|
|
|
wait_args.objs = (uintptr_t)&stress_mutex;
|
|
|
|
for (i = 0; i < STRESS_LOOPS; ++i) {
|
|
ioctl(stress_device, NTSYNC_IOC_WAIT_ANY, &wait_args);
|
|
|
|
++stress_counter;
|
|
|
|
unlock_mutex(stress_mutex, wait_args.owner, &count);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
TEST(stress_wait)
|
|
{
|
|
struct ntsync_event_args event_args;
|
|
struct ntsync_mutex_args mutex_args;
|
|
pthread_t threads[STRESS_THREADS];
|
|
__u32 signaled, i;
|
|
int ret;
|
|
|
|
stress_device = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
|
|
ASSERT_LE(0, stress_device);
|
|
|
|
mutex_args.owner = 0;
|
|
mutex_args.count = 0;
|
|
stress_mutex = ioctl(stress_device, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
|
|
EXPECT_LE(0, stress_mutex);
|
|
|
|
event_args.manual = 1;
|
|
event_args.signaled = 0;
|
|
stress_start_event = ioctl(stress_device, NTSYNC_IOC_CREATE_EVENT, &event_args);
|
|
EXPECT_LE(0, stress_start_event);
|
|
|
|
for (i = 0; i < STRESS_THREADS; ++i)
|
|
pthread_create(&threads[i], NULL, stress_thread, NULL);
|
|
|
|
ret = ioctl(stress_start_event, NTSYNC_IOC_EVENT_SET, &signaled);
|
|
EXPECT_EQ(0, ret);
|
|
|
|
for (i = 0; i < STRESS_THREADS; ++i) {
|
|
ret = pthread_join(threads[i], NULL);
|
|
EXPECT_EQ(0, ret);
|
|
}
|
|
|
|
EXPECT_EQ(STRESS_LOOPS * STRESS_THREADS, stress_counter);
|
|
|
|
close(stress_start_event);
|
|
close(stress_mutex);
|
|
close(stress_device);
|
|
}
|
|
|
|
TEST_HARNESS_MAIN
|