From 1933f35db64c6686fe86658167b44b491aa71daf Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 6 Mar 2022 14:13:10 -0800 Subject: [PATCH] add basic tests for os::Event/SystemEvent functionality --- libraries/libstratosphere/libstratosphere.mk | 4 + .../source/fs/fs_access_log.cpp | 4 +- .../os_inter_process_event_impl.os.linux.cpp | 7 +- .../os_inter_process_event_impl.os.macos.cpp | 7 +- ...ternal_condition_variable_impl.pthread.cpp | 2 +- .../os_multiple_wait_target_impl.os.linux.cpp | 5 + .../os_multiple_wait_target_impl.os.macos.cpp | 5 + .../impl/os_tick_manager_impl.os.windows.hpp | 4 +- tests/TestOsEvents/Makefile | 51 ++ tests/TestOsEvents/source/test.cpp | 461 ++++++++++++++++++ tests/TestOsEvents/unit_test.mk | 155 ++++++ 11 files changed, 696 insertions(+), 9 deletions(-) create mode 100644 tests/TestOsEvents/Makefile create mode 100644 tests/TestOsEvents/source/test.cpp create mode 100644 tests/TestOsEvents/unit_test.mk diff --git a/libraries/libstratosphere/libstratosphere.mk b/libraries/libstratosphere/libstratosphere.mk index e741ef1f6..e854123b6 100644 --- a/libraries/libstratosphere/libstratosphere.mk +++ b/libraries/libstratosphere/libstratosphere.mk @@ -146,6 +146,10 @@ hos_stratosphere_api.o: CXXFLAGS += -fno-lto init_operator_new.o: CXXFLAGS += -fno-lto init_libnx_shim.os.horizon.o: CXXFLAGS += -fno-lto +ifeq ($(ATMOSPHERE_OS_NAME),windows) +os_%.o: CXXFLAGS += -fno-lto +endif + #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin #--------------------------------------------------------------------------------- diff --git a/libraries/libstratosphere/source/fs/fs_access_log.cpp b/libraries/libstratosphere/source/fs/fs_access_log.cpp index c665aa497..7f5e90745 100644 --- a/libraries/libstratosphere/source/fs/fs_access_log.cpp +++ b/libraries/libstratosphere/source/fs/fs_access_log.cpp @@ -543,7 +543,7 @@ namespace ams::fs::impl { bool IsEnabledFileSystemAccessorAccessLog(const char *mount_name) { /* Get the accessor. */ - impl::FileSystemAccessor *accessor; + impl::FileSystemAccessor *accessor = nullptr; if (R_FAILED(impl::Find(std::addressof(accessor), mount_name))) { return true; } @@ -553,7 +553,7 @@ namespace ams::fs::impl { void EnableFileSystemAccessorAccessLog(const char *mount_name) { /* Get the accessor. */ - impl::FileSystemAccessor *accessor; + impl::FileSystemAccessor *accessor = nullptr; AMS_FS_R_ABORT_UNLESS(impl::Find(std::addressof(accessor), mount_name)); accessor->SetAccessLogEnabled(true); } diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.linux.cpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.linux.cpp index 2fd362396..ae9944996 100644 --- a/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.linux.cpp +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.linux.cpp @@ -41,8 +41,11 @@ namespace ams::os::impl { res = ::ppoll(std::addressof(pfd), 1, ns >= 0 ? std::addressof(ts) : nullptr, nullptr); } while (res < 0 && errno == EINTR); - AMS_ASSERT(res == 0); - return pfd.revents & POLLIN; + AMS_ASSERT(res == 0 || res == 1); + + const bool signaled = pfd.revents & POLLIN; + AMS_ASSERT(signaled == (res == 1)); + return signaled; } } diff --git a/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.macos.cpp b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.macos.cpp index 486a1b8f8..4270eec84 100644 --- a/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.macos.cpp +++ b/libraries/libstratosphere/source/os/impl/os_inter_process_event_impl.os.macos.cpp @@ -48,8 +48,11 @@ namespace ams::os::impl { res = ::poll(std::addressof(pfd), 1, timeout); } while (res < 0 && errno == EINTR); - AMS_ASSERT(res == 0); - return pfd.revents & POLLIN; + AMS_ASSERT(res == 0 || res == 1); + + const bool signaled = pfd.revents & POLLIN; + AMS_ASSERT(signaled == (res == 1)); + return signaled; } } diff --git a/libraries/libstratosphere/source/os/impl/os_internal_condition_variable_impl.pthread.cpp b/libraries/libstratosphere/source/os/impl/os_internal_condition_variable_impl.pthread.cpp index 225bb4901..c18a98bfe 100644 --- a/libraries/libstratosphere/source/os/impl/os_internal_condition_variable_impl.pthread.cpp +++ b/libraries/libstratosphere/source/os/impl/os_internal_condition_variable_impl.pthread.cpp @@ -62,7 +62,7 @@ namespace ams::os::impl { const auto res = pthread_cond_timedwait(std::addressof(m_pthread_cond), std::addressof(cs->Get()->m_pthread_mutex), std::addressof(ts)); if (res != 0) { - AMS_ABORT_UNLESS(errno == ETIMEDOUT); + AMS_ABORT_UNLESS(res == ETIMEDOUT); return ConditionVariableStatus::TimedOut; } diff --git a/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.linux.cpp b/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.linux.cpp index 1045ba72c..83750fbea 100644 --- a/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.linux.cpp +++ b/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.linux.cpp @@ -93,4 +93,9 @@ namespace ams::os::impl { } } + Result MultiWaitLinuxImpl::ReplyAndReceiveImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns, NativeHandle reply_target) { + AMS_UNUSED(out_index, num, arr, array_size, ns, reply_target); + R_ABORT_UNLESS(os::ResultNotImplemented()); + } + } diff --git a/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.macos.cpp b/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.macos.cpp index 923bb46c4..31d92d5b7 100644 --- a/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.macos.cpp +++ b/libraries/libstratosphere/source/os/impl/os_multiple_wait_target_impl.os.macos.cpp @@ -97,4 +97,9 @@ namespace ams::os::impl { } } + Result MultiWaitMacosImpl::ReplyAndReceiveImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns, NativeHandle reply_target) { + AMS_UNUSED(out_index, num, arr, array_size, ns, reply_target); + R_ABORT_UNLESS(os::ResultNotImplemented()); + } + } diff --git a/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.os.windows.hpp b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.os.windows.hpp index 50dc0a599..e09ca29fd 100644 --- a/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.os.windows.hpp +++ b/libraries/libstratosphere/source/os/impl/os_tick_manager_impl.os.windows.hpp @@ -50,7 +50,7 @@ namespace ams::os::impl { ALWAYS_INLINE Tick GetTick() const { LARGE_INTEGER freq; - ::QueryPerformanceFrequency(std::addressof(freq)); + ::QueryPerformanceCounter(std::addressof(freq)); return Tick(static_cast(freq.QuadPart)); } @@ -58,7 +58,7 @@ namespace ams::os::impl { LARGE_INTEGER freq; PerformOrderingForGetSystemTickOrdered(); - ::QueryPerformanceFrequency(std::addressof(freq)); + ::QueryPerformanceCounter(std::addressof(freq)); PerformOrderingForGetSystemTickOrdered(); return Tick(static_cast(freq.QuadPart)); diff --git a/tests/TestOsEvents/Makefile b/tests/TestOsEvents/Makefile new file mode 100644 index 000000000..4f16374da --- /dev/null +++ b/tests/TestOsEvents/Makefile @@ -0,0 +1,51 @@ +ATMOSPHERE_BUILD_CONFIGS := +all: nx_release + +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): + @echo "Building $(strip $1)" + @$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5) + +clean-$(strip $1): + @echo "Cleaning $(strip $1)" + @$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5) + +endef + +define ATMOSPHERE_ADD_TARGETS + +$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, $(strip $2)release, $(strip $3), $(strip $4), \ + ATMOSPHERE_BUILD_SETTINGS="$(strip $5)" $(strip $6) \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, $(strip $2)debug, $(strip $3), $(strip $4), \ + ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 $(strip $6) \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, $(strip $2)audit, $(strip $3), $(strip $4), \ + ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 $(strip $6) \ +)) + +endef + + +$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, , nx-hac-001, arm-cortex-a57,,)) + +$(eval $(call ATMOSPHERE_ADD_TARGETS, win_x64, , generic_windows, generic_x64,,)) + +$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64, , generic_linux, generic_x64,,)) +$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64_clang, clang_, generic_linux, generic_x64,, ATMOSPHERE_COMPILER_NAME="clang")) +$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_arm64_clang, clang_, generic_linux, generic_arm64,, ATMOSPHERE_COMPILER_NAME="clang")) + +$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_x64, , generic_macos, generic_x64,,)) +$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_arm64, , generic_macos, generic_arm64,,)) + +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config)) diff --git a/tests/TestOsEvents/source/test.cpp b/tests/TestOsEvents/source/test.cpp new file mode 100644 index 000000000..e3533efe9 --- /dev/null +++ b/tests/TestOsEvents/source/test.cpp @@ -0,0 +1,461 @@ +#include + +namespace ams { + + namespace { + + struct InterThreadSync { + util::Atomic reader_state; + util::Atomic writer_state; + os::EventType writer_ready_event; + os::EventType reader_ready_event; + union { + struct { + os::SystemEventType system_event_as_manual_clear_event; + os::SystemEventType system_event_as_manual_clear_interprocess_event; + os::SystemEventType system_event_as_auto_clear_event; + os::SystemEventType system_event_as_auto_clear_interprocess_event; + }; + os::SystemEventType system_events[4]; + }; + }; + + bool IsManualClearEventIndex(size_t i) { + return i == 0 || i == 1; + } + + alignas(os::MemoryPageSize) constinit u8 g_writer_thread_stack[16_KB]; + alignas(os::MemoryPageSize) constinit u8 g_reader_thread_stack[16_KB]; + + void TestWriterThread(void *arg) { + /* Get the synchronization arguments. */ + auto &sync = *static_cast(arg); + AMS_UNUSED(sync); + + /* Wait for reader to be ready. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 1); + + /* Verify that all events can be signaled. */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 1; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 1); + } + + /* Verify that all events can be signaled (for TimedWait 0). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 2; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 2); + } + + /* Verify that all events can be signaled (for TimedWait 2). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 3; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 3); + } + + /* Verify that all events can be signaled (for True Wait). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 4; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 4); + } + + /* Verify that all events can be signaled (TryWaitAny). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 5; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 5); + } + + /* Verify that all events can be signaled (TimedWaitAny 0). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 6; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 6); + } + + /* Verify that all events can be signaled (TimedWaitAny 2). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 7; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 7); + } + + /* Verify that all events can be signaled (TrueWaitAny). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 8; + os::SignalEvent(std::addressof(sync.writer_ready_event)); + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 8); + } + + /* Verify that reader can receive without explicit sync. */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Set the event for this go. */ + os::SignalSystemEvent(sync.system_events + i); + + sync.writer_state = 9; + + } + + /* Wait for the reader to finish. */ + os::WaitEvent(std::addressof(sync.reader_ready_event)); + AMS_ABORT_UNLESS(sync.reader_state == 9); + } + + void TestReaderThread(void *arg) { + /* Get the synchronization arguments. */ + auto &sync = *static_cast(arg); + AMS_UNUSED(sync); + + /* Set up multi-wait objects. */ + os::MultiWaitType mw; + os::MultiWaitHolderType holders[util::size(sync.system_events)]; + os::InitializeMultiWait(std::addressof(mw)); + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + os::InitializeMultiWaitHolder(holders + i, sync.system_events + i); + os::LinkMultiWaitHolder(std::addressof(mw), holders + i); + } + ON_SCOPE_EXIT { + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + os::UnlinkMultiWaitHolder(holders + i); + os::FinalizeMultiWaitHolder(holders + i); + } + os::FinalizeMultiWait(std::addressof(mw)); + }; + + /* Sanity check: all events are non-signaled. */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + i) == false); + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + i, TimeSpan::FromNanoSeconds(0)) == false); + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + i, TimeSpan::FromMilliSeconds(2)) == false); + } + + /* Sanity check that wait any does the right thing when nothing is signaled. */ + AMS_ABORT_UNLESS(os::TryWaitAny(std::addressof(mw)) == nullptr); + AMS_ABORT_UNLESS(os::TimedWaitAny(std::addressof(mw), TimeSpan::FromNanoSeconds(0)) == nullptr); + AMS_ABORT_UNLESS(os::TimedWaitAny(std::addressof(mw), TimeSpan::FromNanoSeconds(2)) == nullptr); + + /* Let writer know that we're ready. */ + sync.reader_state = 1; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + + /* Verify that we can receive signal on each event. */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 1); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + if (i == n) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true); + if (IsManualClearEventIndex(n)) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } else { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + } else { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + } + + /* Let writer know we're done. */ + sync.reader_state = 1; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive signal on each event (Timed Wait 0). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 2); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + if (i == n) { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == true); + if (IsManualClearEventIndex(n)) { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == true); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == false); + } else { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == false); + } + } else { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == false); + } + } + + /* Let writer know we're done. */ + sync.reader_state = 2; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive signal on each event (Timed Wait 2). */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 3); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + if (i == n) { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == true); + if (IsManualClearEventIndex(n)) { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == true); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == false); + } else { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == false); + } + } else { + AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == false); + } + } + + /* Let writer know we're done. */ + sync.reader_state = 3; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive signal on each event. */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 4); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + if (i == n) { + os::WaitSystemEvent(sync.system_events + n); + if (IsManualClearEventIndex(n)) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true); + os::WaitSystemEvent(sync.system_events + n); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } else { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + } else { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + } + + /* Let writer know we're done. */ + sync.reader_state = 4; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive signal on each event (TryWaitAny) */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 5); + + /* Get the signaled holder. */ + auto *signaled = os::TryWaitAny(std::addressof(mw)); + AMS_ABORT_UNLESS(signaled == holders + i); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n)); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + + /* Let writer know we're done. */ + sync.reader_state = 5; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive signal on each event (TimedWaitAny 0) */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 6); + + /* Get the signaled holder. */ + auto *signaled = os::TimedWaitAny(std::addressof(mw), TimeSpan::FromMilliSeconds(0)); + AMS_ABORT_UNLESS(signaled == holders + i); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n)); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + + /* Let writer know we're done. */ + sync.reader_state = 6; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive signal on each event (TimedWaitAny 2) */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 7); + + /* Get the signaled holder. */ + auto *signaled = os::TimedWaitAny(std::addressof(mw), TimeSpan::FromMilliSeconds(2)); + AMS_ABORT_UNLESS(signaled == holders + i); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n)); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + + /* Let writer know we're done. */ + sync.reader_state = 7; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive signal on each event (True WaitAny) */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + /* Wait for writer to do the relevant work */ + os::WaitEvent(std::addressof(sync.writer_ready_event)); + AMS_ABORT_UNLESS(sync.writer_state == 8); + + /* Get the signaled holder. */ + auto *signaled = os::WaitAny(std::addressof(mw)); + AMS_ABORT_UNLESS(signaled == holders + i); + + /* Test all events. */ + for (size_t n = 0; n < util::size(sync.system_events); ++n) { + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n)); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + + /* Let writer know we're done. */ + sync.reader_state = 8; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + /* Verify that we can receive wait-any signals without sync. */ + for (size_t i = 0; i < util::size(sync.system_events); ++i) { + auto *signaled = os::WaitAny(std::addressof(mw)); + AMS_ABORT_UNLESS(signaled != nullptr); + const size_t n = signaled - holders; + AMS_ABORT_UNLESS(n < util::size(sync.system_events)); + + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true); + os::ClearSystemEvent(sync.system_events + n); + AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false); + } + + AMS_ABORT_UNLESS(os::TryWaitAny(std::addressof(mw)) == nullptr); + + /* Let writer know we're done. */ + sync.reader_state = 9; + os::SignalEvent(std::addressof(sync.reader_ready_event)); + } + + } + + + void Main() { + printf("Doing OS Event tests!\n"); + { + /* Create the synchronization state. */ + InterThreadSync sync_state; + sync_state.reader_state = 0; + sync_state.writer_state = 0; + os::InitializeEvent(std::addressof(sync_state.writer_ready_event), false, os::EventClearMode_AutoClear); + os::InitializeEvent(std::addressof(sync_state.reader_ready_event), false, os::EventClearMode_AutoClear); + + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_manual_clear_event), os::EventClearMode_ManualClear, false)); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_manual_clear_interprocess_event), os::EventClearMode_ManualClear, true)); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_auto_clear_event), os::EventClearMode_AutoClear, false)); + R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_auto_clear_interprocess_event), os::EventClearMode_AutoClear, true)); + + /* Ensure we clean up the sync-state when done. */ + ON_SCOPE_EXIT { + os::FinalizeEvent(std::addressof(sync_state.writer_ready_event)); + os::FinalizeEvent(std::addressof(sync_state.reader_ready_event)); + + os::DestroySystemEvent(std::addressof(sync_state.system_event_as_manual_clear_event)); + os::DestroySystemEvent(std::addressof(sync_state.system_event_as_manual_clear_interprocess_event)); + os::DestroySystemEvent(std::addressof(sync_state.system_event_as_auto_clear_event)); + os::DestroySystemEvent(std::addressof(sync_state.system_event_as_auto_clear_interprocess_event)); + }; + + /* Create the threads. */ + os::ThreadType reader_thread, writer_thread; + R_ABORT_UNLESS(os::CreateThread(std::addressof(reader_thread), TestReaderThread, std::addressof(sync_state), g_reader_thread_stack, sizeof(g_reader_thread_stack), os::DefaultThreadPriority)); + R_ABORT_UNLESS(os::CreateThread(std::addressof(writer_thread), TestWriterThread, std::addressof(sync_state), g_writer_thread_stack, sizeof(g_writer_thread_stack), os::DefaultThreadPriority)); + os::SetThreadNamePointer(std::addressof(reader_thread), "ReaderThread"); + os::SetThreadNamePointer(std::addressof(writer_thread), "WriterThread"); + + /* Start the threads. */ + os::StartThread(std::addressof(reader_thread)); + os::StartThread(std::addressof(writer_thread)); + + /* Wait for the threads to complete. */ + os::WaitThread(std::addressof(reader_thread)); + os::WaitThread(std::addressof(writer_thread)); + + /* Destroy the threads. */ + os::WaitThread(std::addressof(reader_thread)); + os::WaitThread(std::addressof(writer_thread)); + } + printf("All tests completed!\n"); + } + +} \ No newline at end of file diff --git a/tests/TestOsEvents/unit_test.mk b/tests/TestOsEvents/unit_test.mk new file mode 100644 index 000000000..508777b98 --- /dev/null +++ b/tests/TestOsEvents/unit_test.mk @@ -0,0 +1,155 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +ifeq ($(ATMOSPHERE_BOARD),nx-hac-001) +export BOARD_TARGET_SUFFIX := .kip +else ifeq ($(ATMOSPHERE_BOARD),generic_windows) +export BOARD_TARGET_SUFFIX := .exe +else ifeq ($(ATMOSPHERE_BOARD),generic_linux) +export BOARD_TARGET_SUFFIX := +else ifeq ($(ATMOSPHERE_BOARD),generic_macos) +export BOARD_TARGET_SUFFIX := +else +export BOARD_TARGET_SUFFIX := $(TARGET) +endif + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(__RECURSIVE__),1) +#--------------------------------------------------------------------------------- + +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: clean all check_lib + +#--------------------------------------------------------------------------------- +all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + @$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \ + DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ + --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ + -f $(THIS_MAKEFILE) + +$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib + @$(SILENTCMD)echo "Checked library." + +check_lib: + @$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk + +$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR): + @[ -d $@ ] || mkdir -p $@ + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(BOARD_TARGET) $(TARGET).elf + @for i in $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR); do [ -d $$i ] && rmdir --ignore-fail-on-non-empty $$i || true; done + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT)$(BOARD_TARGET_SUFFIX) + +%.kip : %.elf + +%.nsp : %.nso %.npdm + +%.nso: %.elf + + +#--------------------------------------------------------------------------------- +$(OUTPUT).elf: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + @echo linking $(notdir $@) + $(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + $(SILENTCMD)$(NM) -CSn $@ > $(notdir $(OUTPUT).lst) + +$(OUTPUT).exe: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + @echo linking $(notdir $@) + $(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + $(SILENTCMD)$(NM) -CSn $@ > $(notdir $*.lst) + + +ifeq ($(strip $(BOARD_TARGET_SUFFIX)),) +$(OUTPUT): $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + @echo linking $(notdir $@) + $(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + $(SILENTCMD)$(NM) -CSn $@ > $(notdir $@.lst) +endif + +%.npdm : %.npdm.json + @echo built ... $< $@ + @npdmtool $< $@ + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#---------------------------------------------------------------------------------------