diff --git a/include/libtransistor/condvar.h b/include/libtransistor/condvar.h new file mode 100644 index 00000000..6b57c8ce --- /dev/null +++ b/include/libtransistor/condvar.h @@ -0,0 +1,48 @@ +/** + * @file libtransistor/condvar.h + * @brief Condition variables + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct { + uint32_t key; +} trn_condvar_t; + +#define TRN_CONDVAR_STATIC_INITIALIZER {.key = 0} + +/** + * @brief Creates a condition variable + * + * Alternatively, a statically allocated condition variable + * may be initialized with /ref TRN_CONDVAR_STATIC_INITIALIZER + */ +void trn_condvar_create(trn_condvar_t *condvar); + +/** + * @brief Signals n threads waiting on the condition variable, or -1 for all of them + */ +result_t trn_condvar_signal(trn_condvar_t *condvar, int n); + +/** + * @brief Waits for the condition variable to be signalled + * + * @param timeout How long (in nanoseconds) to wait, or -1 for no timeout + */ +result_t trn_condvar_wait(trn_condvar_t *condvar, trn_mutex_t *mutex, uint64_t timeout) REQUIRES(mutex); + +/** + * @brief Destroys a condition variable + */ +void trn_condvar_destroy(trn_condvar_t *condvar); + +#ifdef __cplusplus +} +#endif diff --git a/include/libtransistor/nx.h b/include/libtransistor/nx.h index 960c7cec..f55449cd 100644 --- a/include/libtransistor/nx.h +++ b/include/libtransistor/nx.h @@ -24,6 +24,7 @@ extern "C" { #include #include #include +#include #include // filesystem diff --git a/lib/condvar.c b/lib/condvar.c new file mode 100644 index 00000000..9863b2e5 --- /dev/null +++ b/lib/condvar.c @@ -0,0 +1,28 @@ +#include + +#include +#include + +void trn_condvar_create(trn_condvar_t *condvar) { + condvar->key = 0; +} + +result_t trn_condvar_signal(trn_condvar_t *condvar, int n) { + return svcSignalProcessWideKey(&condvar->key, n); +} + +result_t trn_condvar_wait(trn_condvar_t *condvar, trn_mutex_t *mutex, uint64_t timeout) NO_THREAD_SAFETY_ANALYSIS { + result_t r; + + r = svcWaitProcessWideKeyAtomic((void*) &mutex->lock, &condvar->key, get_thread_handle(), timeout); + + if(r == 0xea01) { + trn_mutex_lock(mutex); + } + + return r; +} + +void trn_condvar_destroy(trn_condvar_t *condvar) { + condvar->key = -1; +} diff --git a/mk/tests.mk b/mk/tests.mk index c4bc75af..c0f29b58 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -1,6 +1,6 @@ # LIBTRANSISTOR TESTS -libtransistor_TESTS := malloc bsd_ai_packing bsd sfdnsres nv helloworld hid hexdump args ssp stdin vi gpu display am sqfs_img audio_output init_fini_arrays ipc_server pthread ipc_fs fs_stress cpp unwind cpp_exceptions cpp_dynamic_memory hid_init_stress usb usb_serial thread mutex override_heap # fs_release_inodes +libtransistor_TESTS := malloc bsd_ai_packing bsd sfdnsres nv helloworld hid hexdump args ssp stdin vi gpu display am sqfs_img audio_output init_fini_arrays ipc_server pthread ipc_fs fs_stress cpp unwind cpp_exceptions cpp_dynamic_memory hid_init_stress usb usb_serial thread mutex override_heap condvar # fs_release_inodes libtransistor_DYNAMIC_TESTS := simple dlfcn bad_resolution preemption elf # RUN RULES diff --git a/mk/transistor_headers.mk b/mk/transistor_headers.mk index 70ee31b0..d2db85f0 100644 --- a/mk/transistor_headers.mk +++ b/mk/transistor_headers.mk @@ -5,6 +5,7 @@ libtransistor_HEADER_NAMES := \ alloc_pages.h \ audio.h \ collections/list.h \ + condvar.h \ display/binder.h \ display/display.h \ display/fence.h \ diff --git a/mk/transistor_objects.mk b/mk/transistor_objects.mk index cbe3a158..9201abb6 100644 --- a/mk/transistor_objects.mk +++ b/mk/transistor_objects.mk @@ -4,6 +4,7 @@ libtransistor_OBJECT_NAMES := \ address_space.o \ alloc_pages.o \ + condvar.o \ crt0_common.o \ display/binder.o \ display/display.o \ diff --git a/test/test_condvar.c b/test/test_condvar.c new file mode 100644 index 00000000..d9a3f7a9 --- /dev/null +++ b/test/test_condvar.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include + +#include + +static trn_mutex_t mutex; +static trn_condvar_t condvar; +static volatile bool pred = 0; + +void other_thread(void *arg) { + printf("O: other thread started\n"); + + printf("O: trying to lock mutex\n"); + trn_mutex_lock(&mutex); + printf("O: acquired mutex\n"); + while(!pred) { + printf("O: waiting on condvar...\n"); + trn_condvar_wait(&condvar, &mutex, -1); + printf("O: woke up\n"); + } + trn_mutex_unlock(&mutex); +} + +int main(int argc, char *argv[]) { + result_t r; + trn_mutex_create(&mutex); + trn_condvar_create(&condvar); + + trn_thread_t thread; + ASSERT_OK(fail, trn_thread_create(&thread, other_thread, NULL, -1, -2, 1024 * 64, NULL)); + ASSERT_OK(fail_thread, trn_thread_start(&thread)); + + printf("M: started other thread\n"); + svcSleepThread(5000000000); + printf("M: signalling spuriously\n"); + trn_condvar_signal(&condvar, 1); + svcSleepThread(5000000000); + printf("M: signalling\n"); + pred = 1; + trn_condvar_signal(&condvar, 1); + + trn_thread_join(&thread, -1); + +fail_thread: + trn_thread_destroy(&thread); +fail: + return r; +}