diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp index 85462c611..9000dcc68 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_condition_variable.hpp @@ -64,6 +64,8 @@ namespace ams::kern { this->tree.insert(*thread); } + private: + KThread *SignalImpl(KThread *thread); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 6974eb40c..9eb2e52f1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -192,6 +192,22 @@ namespace ams::kern { void SetPreemptionState(); + Result SignalToAddress(KProcessAddress address) { + return this->cond_var.SignalToAddress(address); + } + + Result WaitForAddress(ams::svc::Handle handle, KProcessAddress address, u32 tag) { + return this->cond_var.WaitForAddress(handle, address, tag); + } + + void SignalConditionVariable(uintptr_t cv_key, int32_t count) { + return this->cond_var.Signal(cv_key, count); + } + + Result WaitConditionVariable(KProcessAddress address, uintptr_t cv_key, u32 tag, s64 ns) { + return this->cond_var.Wait(address, cv_key, tag, ns); + } + static void Switch(KProcess *cur_process, KProcess *next_process) { /* Set the current process pointer. */ SetCurrentProcess(next_process); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index b7bb472dd..cb0731037 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -282,6 +282,15 @@ namespace ams::kern { constexpr uintptr_t GetConditionVariableKey() const { return this->condvar_key; } + constexpr void SetupForConditionVariableCompare(uintptr_t cv_key, int priority) { + this->condvar_key = cv_key; + this->priority = priority; + } + + void ClearConditionVariable() { + this->cond_var = nullptr; + } + constexpr s32 GetIdealCore() const { return this->ideal_core_id; } constexpr s32 GetActiveCore() const { return this->core_id; } constexpr void SetActiveCore(s32 core) { this->core_id = core; } diff --git a/libraries/libmesosphere/source/kern_k_condition_variable.cpp b/libraries/libmesosphere/source/kern_k_condition_variable.cpp new file mode 100644 index 000000000..34ad03687 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_condition_variable.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern { + + namespace { + + constinit KThread g_cv_compare_thread; + + } + + Result KConditionVariable::SignalToAddress(KProcessAddress addr) { + MESOSPHERE_UNIMPLEMENTED(); + } + + Result KConditionVariable::WaitForAddress(ams::svc::Handle handle, KProcessAddress addr, u32 value) { + MESOSPHERE_UNIMPLEMENTED(); + } + + KThread *KConditionVariable::SignalImpl(KThread *thread) { + MESOSPHERE_UNIMPLEMENTED(); + } + + void KConditionVariable::Signal(uintptr_t cv_key, s32 count) { + /* Prepare for signaling. */ + constexpr int MaxThreads = 16; + KLinkedList thread_list; + KThread *thread_array[MaxThreads]; + int num_to_close = 0; + + /* Perform signaling. */ + int num_waiters = 0; + { + KScopedSchedulerLock sl; + g_cv_compare_thread.SetupForConditionVariableCompare(cv_key, -1); + + auto it = this->tree.nfind(g_cv_compare_thread); + while ((it != this->tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { + KThread *target_thread = std::addressof(*it); + + if (KThread *thread = this->SignalImpl(target_thread); thread != nullptr) { + if (num_to_close < MaxThreads) { + thread_array[num_to_close++] = thread; + } else { + thread_list.push_back(*thread); + } + } + + it = this->tree.erase(it); + target_thread->ClearConditionVariable(); + ++num_waiters; + } + } + + /* Close threads in the array. */ + for (auto i = 0; i < num_to_close; ++i) { + thread_array[i]->Close(); + } + + /* Close threads in the list. */ + if (num_waiters > MaxThreads) { + auto it = thread_list.begin(); + while (it != thread_list.end()) { + KThread *thread = std::addressof(*it); + thread->Close(); + it = thread_list.erase(it); + } + } + } + + Result KConditionVariable::Wait(KProcessAddress addr, uintptr_t key, u32 value, s64 timeout) { + MESOSPHERE_UNIMPLEMENTED(); + } + +} diff --git a/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp b/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp index 8307dccae..0a3522421 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_condition_variable.cpp @@ -21,7 +21,9 @@ namespace ams::kern::svc { namespace { - + void SignalProcessWideKey(uintptr_t cv_key, int32_t count) { + return GetCurrentProcess().SignalConditionVariable(util::AlignDown(cv_key, sizeof(u32)), count); + } } @@ -32,7 +34,7 @@ namespace ams::kern::svc { } void SignalProcessWideKey64(ams::svc::Address cv_key, int32_t count) { - MESOSPHERE_PANIC("Stubbed SvcSignalProcessWideKey64 was called."); + return SignalProcessWideKey(cv_key, count); } /* ============================= 64From32 ABI ============================= */ @@ -42,7 +44,7 @@ namespace ams::kern::svc { } void SignalProcessWideKey64From32(ams::svc::Address cv_key, int32_t count) { - MESOSPHERE_PANIC("Stubbed SvcSignalProcessWideKey64From32 was called."); + return SignalProcessWideKey(cv_key, count); } }