Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344// 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