os: refactor/rewrite entire namespace.

This commit is contained in:
Michael Scire 2020-04-08 02:21:35 -07:00
parent 6193283f03
commit 065485b971
181 changed files with 5353 additions and 1929 deletions

View file

@ -324,28 +324,20 @@ namespace ams::fatal {
bool is_creport; bool is_creport;
CpuContext cpu_ctx; CpuContext cpu_ctx;
bool generate_error_report; bool generate_error_report;
Event erpt_event; os::Event *erpt_event;
Event battery_event; os::Event *battery_event;
size_t stack_dump_size; size_t stack_dump_size;
u64 stack_dump_base; u64 stack_dump_base;
u8 stack_dump[0x100]; u8 stack_dump[0x100];
u64 tls_address; u64 tls_address;
u8 tls_dump[0x100]; u8 tls_dump[0x100];
void ClearState() { ThrowContext(os::Event *erpt, os::Event *bat)
this->result = ResultSuccess(); : result(ResultSuccess()), program_id(), proc_name(), is_creport(), cpu_ctx(), generate_error_report(),
this->program_id = ncm::ProgramId::Invalid; erpt_event(erpt), battery_event(bat),
std::memset(this->proc_name, 0, sizeof(this->proc_name)); stack_dump_size(), stack_dump_base(), stack_dump(), tls_address(), tls_dump()
this->is_creport = false; {
std::memset(&this->cpu_ctx, 0, sizeof(this->cpu_ctx)); /* ... */
this->generate_error_report = false;
std::memset(&this->erpt_event, 0, sizeof(this->erpt_event));
std::memset(&this->battery_event, 0, sizeof(this->battery_event));
this->stack_dump_size = 0;
this->stack_dump_base = 0;
std::memset(this->stack_dump, 0, sizeof(this->stack_dump));
this->tls_address = 0;
std::memset(this->tls_dump, 0, sizeof(this->tls_dump));
} }
}; };

View file

@ -63,7 +63,7 @@ namespace ams::fs {
s64 size; s64 size;
os::Mutex mutex; os::Mutex mutex;
public: public:
constexpr explicit FileHandleStorage(FileHandle handle, bool close_file) : handle(handle), close_file(close_file), size(InvalidSize), mutex() { /* ... */ } constexpr explicit FileHandleStorage(FileHandle handle, bool close_file) : handle(handle), close_file(close_file), size(InvalidSize), mutex(false) { /* ... */ }
constexpr explicit FileHandleStorage(FileHandle handle) : FileHandleStorage(handle, false) { /* ... */ } constexpr explicit FileHandleStorage(FileHandle handle) : FileHandleStorage(handle, false) { /* ... */ }
virtual ~FileHandleStorage() override { virtual ~FileHandleStorage() override {

View file

@ -70,7 +70,7 @@ namespace ams::kvdb {
Path GetPath(const void *key, size_t key_size); Path GetPath(const void *key, size_t key_size);
Result GetKey(size_t *out_size, void *out_key, size_t max_out_size, const FileName &file_name); Result GetKey(size_t *out_size, void *out_key, size_t max_out_size, const FileName &file_name);
public: public:
FileKeyValueStore() { /* ... */ } FileKeyValueStore() : lock(false) { /* ... */ }
/* Basic accessors. */ /* Basic accessors. */
Result Initialize(const char *dir); Result Initialize(const char *dir);

View file

@ -85,7 +85,7 @@ namespace ams::lmem::impl {
void *heap_start; void *heap_start;
void *heap_end; void *heap_end;
os::Mutex mutex; os::MutexType mutex;
u8 option; u8 option;
ImplementationHeapHead impl_head; ImplementationHeapHead impl_head;
}; };

View file

@ -28,7 +28,7 @@ namespace ams::lr {
std::shared_ptr<IRegisteredLocationResolver> registered_location_resolver = nullptr; std::shared_ptr<IRegisteredLocationResolver> registered_location_resolver = nullptr;
std::shared_ptr<IAddOnContentLocationResolver> add_on_content_location_resolver = nullptr; std::shared_ptr<IAddOnContentLocationResolver> add_on_content_location_resolver = nullptr;
os::Mutex mutex; os::Mutex mutex{false};
public: public:
/* Actual commands. */ /* Actual commands. */
virtual Result OpenLocationResolver(sf::Out<std::shared_ptr<ILocationResolver>> out, ncm::StorageId storage_id) override; virtual Result OpenLocationResolver(sf::Out<std::shared_ptr<ILocationResolver>> out, ncm::StorageId storage_id) override;

View file

@ -83,7 +83,7 @@ namespace ams::ncm {
ContentMetaDatabaseRoot() { /* ... */ } ContentMetaDatabaseRoot() { /* ... */ }
}; };
private: private:
os::RecursiveMutex mutex; os::Mutex mutex;
bool initialized; bool initialized;
ContentStorageRoot content_storage_roots[MaxContentStorageRoots]; ContentStorageRoot content_storage_roots[MaxContentStorageRoots];
ContentMetaDatabaseRoot content_meta_database_roots[MaxContentMetaDatabaseRoots]; ContentMetaDatabaseRoot content_meta_database_roots[MaxContentMetaDatabaseRoots];
@ -91,7 +91,7 @@ namespace ams::ncm {
u32 num_content_meta_entries; u32 num_content_meta_entries;
RightsIdCache rights_id_cache; RightsIdCache rights_id_cache;
public: public:
ContentManagerImpl() : initialized(false) { /* ... */ }; ContentManagerImpl() : mutex(true), initialized(false) { /* ... */ };
~ContentManagerImpl(); ~ContentManagerImpl();
public: public:
Result Initialize(const ContentManagerConfig &config); Result Initialize(const ContentManagerConfig &config);

View file

@ -102,7 +102,7 @@ namespace ams::ncm {
return result; return result;
} }
public: public:
InstallTaskBase() : data(), progress(), cancel_requested() { /* ... */ } InstallTaskBase() : data(), progress(), progress_mutex(false), cancel_mutex(false), cancel_requested(), throughput_mutex(false) { /* ... */ }
virtual ~InstallTaskBase() { /* ... */ }; virtual ~InstallTaskBase() { /* ... */ };
public: public:
virtual void Cancel(); virtual void Cancel();

View file

@ -37,7 +37,7 @@ namespace ams::ncm {
u64 counter; u64 counter;
os::Mutex mutex; os::Mutex mutex;
public: public:
RightsIdCache() { RightsIdCache() : mutex(false) {
this->Invalidate(); this->Invalidate();
} }

View file

@ -26,15 +26,13 @@
#include <stratosphere/os/os_process_handle.hpp> #include <stratosphere/os/os_process_handle.hpp>
#include <stratosphere/os/os_random.hpp> #include <stratosphere/os/os_random.hpp>
#include <stratosphere/os/os_mutex.hpp> #include <stratosphere/os/os_mutex.hpp>
#include <stratosphere/os/os_condvar.hpp> #include <stratosphere/os/os_condition_variable.hpp>
#include <stratosphere/os/os_rw_lock.hpp> #include <stratosphere/os/os_rw_lock.hpp>
#include <stratosphere/os/os_semaphore.hpp> #include <stratosphere/os/os_semaphore.hpp>
#include <stratosphere/os/os_timeout_helper.hpp>
#include <stratosphere/os/os_event.hpp> #include <stratosphere/os/os_event.hpp>
#include <stratosphere/os/os_system_event.hpp> #include <stratosphere/os/os_system_event.hpp>
#include <stratosphere/os/os_interrupt_event.hpp> #include <stratosphere/os/os_interrupt_event.hpp>
#include <stratosphere/os/os_thread_local_storage_api.hpp> #include <stratosphere/os/os_thread_local_storage_api.hpp>
#include <stratosphere/os/os_thread.hpp> #include <stratosphere/os/os_thread.hpp>
#include <stratosphere/os/os_message_queue.hpp> #include <stratosphere/os/os_message_queue.hpp>
#include <stratosphere/os/os_waitable_holder.hpp> #include <stratosphere/os/os_waitable.hpp>
#include <stratosphere/os/os_waitable_manager.hpp>

View file

@ -0,0 +1,58 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_condition_variable_common.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include <stratosphere/os/impl/os_internal_condition_variable_impl.os.horizon.hpp>
#else
#error "Unknown OS for ams::os::impl::InternalConditionVariableImpl"
#endif
namespace ams::os::impl {
class InternalConditionVariable {
private:
InternalConditionVariableImpl impl;
public:
constexpr InternalConditionVariable() : impl() { /* ... */ }
constexpr void Initialize() {
this->impl.Initialize();
}
void Signal() {
this->impl.Signal();
}
void Broadcast() {
this->impl.Broadcast();
}
void Wait(InternalCriticalSection *cs) {
this->impl.Wait(cs);
}
ConditionVariableStatus TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
return this->impl.TimedWait(cs, timeout_helper);
}
};
using InternalConditionVariableStorage = TYPED_STORAGE(InternalConditionVariable);
}

View file

@ -0,0 +1,43 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_condition_variable_common.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
namespace ams::os::impl {
class TimeoutHelper;
class InternalConditionVariableImpl {
private:
u32 value;
public:
constexpr InternalConditionVariableImpl() : value(0) { /* ... */ }
constexpr void Initialize() {
this->value = 0;
}
void Signal();
void Broadcast();
void Wait(InternalCriticalSection *cs);
ConditionVariableStatus TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper);
};
}

View file

@ -0,0 +1,62 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include <stratosphere/os/impl/os_internal_critical_section_impl.os.horizon.hpp>
#else
#error "Unknown OS for ams::os::impl::InternalCriticalSectionImpl"
#endif
namespace ams::os::impl {
class InternalCriticalSection {
private:
InternalCriticalSectionImpl impl;
public:
constexpr InternalCriticalSection() : impl() { /* ... */ }
constexpr void Initialize() { this->impl.Initialize(); }
constexpr void Finalize() { this->impl.Finalize(); }
void Enter() { return this->impl.Enter(); }
bool TryEnter() { return this->impl.TryEnter(); }
void Leave() { return this->impl.Leave(); }
bool IsLockedByCurrentThread() const { return this->impl.IsLockedByCurrentThread(); }
ALWAYS_INLINE void Lock() { return this->Enter(); }
ALWAYS_INLINE bool TryLock() { return this->TryEnter(); }
ALWAYS_INLINE void Unlock() { return this->Leave(); }
ALWAYS_INLINE void lock() { return this->Lock(); }
ALWAYS_INLINE bool try_lock() { return this->TryLock(); }
ALWAYS_INLINE void unlock() { return this->Unlock(); }
InternalCriticalSectionImpl *Get() {
return std::addressof(this->impl);
}
const InternalCriticalSectionImpl *Get() const {
return std::addressof(this->impl);
}
};
using InternalCriticalSectionStorage = TYPED_STORAGE(InternalCriticalSection);
}

View file

@ -0,0 +1,52 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os::impl {
class ReadWriteLockImpl;
class InternalConditionVariableImpl;
class InternalCriticalSectionImpl {
private:
friend class ReadWriteLockImpl;
friend class InternalConditionVariableImpl;
private:
u32 thread_handle;
public:
constexpr InternalCriticalSectionImpl() : thread_handle(svc::InvalidHandle) { /* ... */ }
constexpr void Initialize() { this->thread_handle = svc::InvalidHandle; }
constexpr void Finalize() { /* ... */}
void Enter();
bool TryEnter();
void Leave();
bool IsLockedByCurrentThread() const;
ALWAYS_INLINE void Lock() { return this->Enter(); }
ALWAYS_INLINE bool TryLock() { return this->TryEnter(); }
ALWAYS_INLINE void Unlock() { return this->Leave(); }
ALWAYS_INLINE void lock() { return this->Lock(); }
ALWAYS_INLINE bool try_lock() { return this->TryLock(); }
ALWAYS_INLINE void unlock() { return this->Unlock(); }
};
}

View file

@ -0,0 +1,65 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_mutex_types.hpp>
#include <stratosphere/os/os_condition_variable_common.hpp>
#include <stratosphere/os/os_condition_variable_types.hpp>
#include <stratosphere/os/os_condition_variable_api.hpp>
namespace ams::os {
class ConditionVariable {
NON_COPYABLE(ConditionVariable);
NON_MOVEABLE(ConditionVariable);
private:
ConditionVariableType cv;
public:
constexpr ConditionVariable() : cv{::ams::os::ConditionVariableType::State_Initialized, {{0}}} { /* ... */ }
~ConditionVariable() { FinalizeConditionVariable(std::addressof(this->cv)); }
void Signal() {
SignalConditionVariable(std::addressof(this->cv));
}
void Broadcast() {
BroadcastConditionVariable(std::addressof(this->cv));
}
void Wait(ams::os::MutexType &mutex) {
WaitConditionVariable(std::addressof(this->cv), std::addressof(mutex));
}
ConditionVariableStatus TimedWait(ams::os::MutexType &mutex, TimeSpan timeout) {
return TimedWaitConditionVariable(std::addressof(this->cv), std::addressof(mutex), timeout);
}
operator ConditionVariableType &() {
return this->cv;
}
operator const ConditionVariableType &() const {
return this->cv;
}
ConditionVariableType *GetBase() {
return std::addressof(this->cv);
}
};
}

View file

@ -0,0 +1,35 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_condition_variable_common.hpp>
namespace ams::os {
struct MutexType;
struct ConditionVariableType;
void InitializeConditionVariable(ConditionVariableType *cv);
void FinalizeConditionVariable(ConditionVariableType *cv);
void SignalConditionVariable(ConditionVariableType *cv);
void BroadcastConditionVariable(ConditionVariableType *cv);
void WaitConditionVariable(ConditionVariableType *cv, MutexType *m);
ConditionVariableStatus TimedWaitConditionVariable(ConditionVariableType *cv, MutexType *m, TimeSpan timeout);
}

View file

@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
enum class ConditionVariableStatus {
TimedOut = 0,
Success = 1,
};
}

View file

@ -0,0 +1,37 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
namespace ams::os {
struct ConditionVariableType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
u8 state;
union {
s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)];
impl::InternalConditionVariableStorage _storage;
};
};
static_assert(std::is_trivial<ConditionVariableType>::value);
}

View file

@ -1,68 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "os_mutex.hpp"
namespace ams::os {
enum class ConditionVariableStatus {
TimedOut = 0,
Success = 1,
};
class ConditionVariable {
NON_COPYABLE(ConditionVariable);
NON_MOVEABLE(ConditionVariable);
private:
CondVar cv;
public:
constexpr ConditionVariable() : cv() { /* ... */ }
ConditionVariableStatus TimedWait(::Mutex *m, u64 timeout) {
if (timeout > 0) {
/* Abort on any error other than timed out/success. */
R_TRY_CATCH(condvarWaitTimeout(&this->cv, m, timeout)) {
R_CATCH(svc::ResultTimedOut) { return ConditionVariableStatus::TimedOut; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ConditionVariableStatus::Success;
}
return ConditionVariableStatus::TimedOut;
}
void Wait(::Mutex *m) {
R_ABORT_UNLESS(condvarWait(&this->cv, m));
}
ConditionVariableStatus TimedWait(os::Mutex *m, u64 timeout) {
return this->TimedWait(m->GetMutex(), timeout);
}
void Wait(os::Mutex *m) {
return this->Wait(m->GetMutex());
}
void Signal() {
condvarWakeOne(&this->cv);
}
void Broadcast() {
condvarWakeAll(&this->cv);
}
};
}

View file

@ -15,39 +15,58 @@
*/ */
#pragma once #pragma once
#include "os_mutex.hpp" #include <vapours.hpp>
#include "os_condvar.hpp" #include <stratosphere/os/os_event_common.hpp>
#include "os_timeout_helper.hpp" #include <stratosphere/os/os_event_types.hpp>
#include <stratosphere/os/os_event_api.hpp>
namespace ams::os { namespace ams::os {
namespace impl {
class WaitableObjectList;
class WaitableHolderOfEvent;
}
class Event { class Event {
friend class impl::WaitableHolderOfEvent;
NON_COPYABLE(Event); NON_COPYABLE(Event);
NON_MOVEABLE(Event); NON_MOVEABLE(Event);
private: private:
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage; EventType event;
Mutex lock;
ConditionVariable cv;
u64 counter = 0;
bool auto_clear;
bool signaled;
public: public:
Event(bool a = true, bool s = false); explicit Event(EventClearMode clear_mode) {
~Event(); InitializeEvent(std::addressof(this->event), false, clear_mode);
}
void Signal(); ~Event() {
void Reset(); FinalizeEvent(std::addressof(this->event));
void Wait(); }
bool TryWait();
bool TimedWait(u64 ns); void Wait() {
return WaitEvent(std::addressof(this->event));
}
bool TryWait() {
return TryWaitEvent(std::addressof(this->event));
}
bool TimedWait(TimeSpan timeout) {
return TimedWaitEvent(std::addressof(this->event), timeout);
}
void Signal() {
return SignalEvent(std::addressof(this->event));
}
void Clear() {
return ClearEvent(std::addressof(this->event));
}
operator EventType &() {
return this->event;
}
operator const EventType &() const {
return this->event;
}
EventType *GetBase() {
return std::addressof(this->event);
}
}; };
} }

View file

@ -0,0 +1,37 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_event_common.hpp>
namespace ams::os {
struct EventType;
struct WaitableHolderType;
void InitializeEvent(EventType *event, bool signaled, EventClearMode clear_mode);
void FinalizeEvent(EventType *event);
void SignalEvent(EventType *event);
void WaitEvent(EventType *event);
bool TryWaitEvent(EventType *event);
bool TimedWaitEvent(EventType *event, TimeSpan timeout);
void ClearEvent(EventType *event);
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, EventType *event);
}

View file

@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
enum EventClearMode {
EventClearMode_ManualClear = 0,
EventClearMode_AutoClear = 1,
};
}

View file

@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
namespace ams::os {
namespace impl {
class WaitableObjectList;
}
struct EventType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage;
bool signaled;
bool initially_signaled;
u8 clear_mode;
u8 state;
u32 broadcast_counter_low;
u32 broadcast_counter_high;
impl::InternalCriticalSectionStorage cs_event;
impl::InternalConditionVariableStorage cv_signaled;
};
static_assert(std::is_trivial<EventType>::value);
}

View file

@ -15,35 +15,56 @@
*/ */
#pragma once #pragma once
#include "os_managed_handle.hpp" #include <vapours.hpp>
#include <stratosphere/os/os_event_common.hpp>
#include <stratosphere/os/os_interrupt_event_common.hpp>
#include <stratosphere/os/os_interrupt_event_types.hpp>
#include <stratosphere/os/os_interrupt_event_api.hpp>
namespace ams::os { namespace ams::os {
namespace impl {
class WaitableHolderOfInterruptEvent;
}
class InterruptEvent { class InterruptEvent {
friend class impl::WaitableHolderOfInterruptEvent;
NON_COPYABLE(InterruptEvent); NON_COPYABLE(InterruptEvent);
NON_MOVEABLE(InterruptEvent); NON_MOVEABLE(InterruptEvent);
private: private:
ManagedHandle handle; InterruptEventType event;
bool auto_clear;
bool is_initialized;
public: public:
InterruptEvent() : auto_clear(true), is_initialized(false) { } explicit InterruptEvent(InterruptName name, EventClearMode clear_mode) {
InterruptEvent(u32 interrupt_id, bool autoclear = true); InitializeInterruptEvent(std::addressof(this->event), name, clear_mode);
}
Result Initialize(u32 interrupt_id, bool autoclear = true); ~InterruptEvent() {
void Finalize(); FinalizeInterruptEvent(std::addressof(this->event));
}
void Reset(); void Wait() {
void Wait(); return WaitInterruptEvent(std::addressof(this->event));
bool TryWait(); }
bool TimedWait(u64 ns);
bool TryWait() {
return TryWaitInterruptEvent(std::addressof(this->event));
}
bool TimedWait(TimeSpan timeout) {
return TimedWaitInterruptEvent(std::addressof(this->event), timeout);
}
void Clear() {
return ClearInterruptEvent(std::addressof(this->event));
}
operator InterruptEventType &() {
return this->event;
}
operator const InterruptEventType &() const {
return this->event;
}
InterruptEventType *GetBase() {
return std::addressof(this->event);
}
}; };
} }

View file

@ -0,0 +1,36 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_interrupt_event_common.hpp>
namespace ams::os {
struct InterruptEventType;
struct WaitableHolderType;
void InitializeInterruptEvent(InterruptEventType *event, InterruptName name, EventClearMode clear_mode);
void FinalizeInterruptEvent(InterruptEventType *event);
void WaitInterruptEvent(InterruptEventType *event);
bool TryWaitInterruptEvent(InterruptEventType *event);
bool TimedWaitInterruptEvent(InterruptEventType *event, TimeSpan timeout);
void ClearInterruptEvent(InterruptEventType *event);
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterruptEventType *event);
}

View file

@ -0,0 +1,23 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
using InterruptName = s32;
}

View file

@ -0,0 +1,46 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
namespace ams::os {
namespace impl {
class WaitableObjectList;
class InterruptEventImpl;
}
struct InterruptEventType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage;
u8 clear_mode;
u8 state;
util::TypedStorage<impl::InterruptEventImpl, sizeof(svc::Handle) * 2, alignof(svc::Handle)> impl;
};
static_assert(std::is_trivial<InterruptEventType>::value);
}

View file

@ -15,75 +15,87 @@
*/ */
#pragma once #pragma once
#include "os_mutex.hpp" #include <stratosphere/os/os_message_queue_common.hpp>
#include "os_condvar.hpp" #include <stratosphere/os/os_message_queue_types.hpp>
#include <stratosphere/os/os_message_queue_api.hpp>
namespace ams::os { namespace ams::os {
namespace impl {
class WaitableObjectList;
template<MessageQueueWaitKind WaitKind>
class WaitableHolderOfMessageQueue;
}
class MessageQueue { class MessageQueue {
template<MessageQueueWaitKind WaitKind>
friend class impl::WaitableHolderOfMessageQueue;
NON_COPYABLE(MessageQueue); NON_COPYABLE(MessageQueue);
NON_MOVEABLE(MessageQueue); NON_MOVEABLE(MessageQueue);
private: private:
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitlist_not_empty; MessageQueueType mq;
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitlist_not_full;
Mutex queue_lock;
ConditionVariable cv_not_full;
ConditionVariable cv_not_empty;
std::unique_ptr<uintptr_t[]> buffer;
size_t capacity;
size_t count;
size_t offset;
private:
constexpr inline bool IsFull() const {
return this->count >= this->capacity;
}
constexpr inline bool IsEmpty() const {
return this->count == 0;
}
void SendInternal(uintptr_t data);
void SendNextInternal(uintptr_t data);
uintptr_t ReceiveInternal();
uintptr_t PeekInternal();
public: public:
MessageQueue(std::unique_ptr<uintptr_t[]> buf, size_t c); explicit MessageQueue(uintptr_t *buf, size_t count) {
~MessageQueue(); InitializeMessageQueue(std::addressof(this->mq), buf, count);
}
/* For convenience. */ ~MessageQueue() { FinalizeMessageQueue(std::addressof(this->mq)); }
MessageQueue(size_t c) : MessageQueue(std::make_unique<uintptr_t[]>(c), c) { /* ... */ }
/* Sending (FIFO functionality) */ /* Sending (FIFO functionality) */
void Send(uintptr_t data); void Send(uintptr_t data) {
bool TrySend(uintptr_t data); return SendMessageQueue(std::addressof(this->mq), data);
bool TimedSend(uintptr_t data, u64 timeout); }
bool TrySend(uintptr_t data) {
return TrySendMessageQueue(std::addressof(this->mq), data);
}
bool TimedSend(uintptr_t data, TimeSpan timeout) {
return TimedSendMessageQueue(std::addressof(this->mq), data, timeout);
}
/* Sending (LIFO functionality) */ /* Sending (LIFO functionality) */
void SendNext(uintptr_t data); void SendNext(uintptr_t data) {
bool TrySendNext(uintptr_t data); return SendNextMessageQueue(std::addressof(this->mq), data);
bool TimedSendNext(uintptr_t data, u64 timeout); }
bool TrySendNext(uintptr_t data) {
return TrySendNextMessageQueue(std::addressof(this->mq), data);
}
bool TimedSendNext(uintptr_t data, TimeSpan timeout) {
return TimedSendNextMessageQueue(std::addressof(this->mq), data, timeout);
}
/* Receive functionality */ /* Receive functionality */
void Receive(uintptr_t *out); void Receive(uintptr_t *out) {
bool TryReceive(uintptr_t *out); return ReceiveMessageQueue(out, std::addressof(this->mq));
bool TimedReceive(uintptr_t *out, u64 timeout); }
bool TryReceive(uintptr_t *out) {
return TryReceiveMessageQueue(out, std::addressof(this->mq));
}
bool TimedReceive(uintptr_t *out, TimeSpan timeout) {
return TimedReceiveMessageQueue(out, std::addressof(this->mq), timeout);
}
/* Peek functionality */ /* Peek functionality */
void Peek(uintptr_t *out); void Peek(uintptr_t *out) const {
bool TryPeek(uintptr_t *out); return PeekMessageQueue(out, std::addressof(this->mq));
bool TimedPeek(uintptr_t *out, u64 timeout); }
bool TryPeek(uintptr_t *out) const {
return TryPeekMessageQueue(out, std::addressof(this->mq));
}
bool TimedPeek(uintptr_t *out, TimeSpan timeout) const {
return TimedPeekMessageQueue(out, std::addressof(this->mq), timeout);
}
operator MessageQueueType &() {
return this->mq;
}
operator const MessageQueueType &() const {
return this->mq;
}
MessageQueueType *GetBase() {
return std::addressof(this->mq);
}
}; };
} }

View file

@ -0,0 +1,51 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_message_queue_common.hpp>
namespace ams::os {
struct MessageQueueType;
struct WaitableHolderType;
void InitializeMessageQueue(MessageQueueType *mq, uintptr_t *buffer, size_t count);
void FinalizeMessageQueue(MessageQueueType *mq);
/* Sending (FIFO functionality) */
void SendMessageQueue(MessageQueueType *mq, uintptr_t data);
bool TrySendMessageQueue(MessageQueueType *mq, uintptr_t data);
bool TimedSendMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout);
/* Sending (LIFO functionality) */
void SendNextMessageQueue(MessageQueueType *mq, uintptr_t data);
bool TrySendNextMessageQueue(MessageQueueType *mq, uintptr_t data);
bool TimedSendNextMessageQueue(MessageQueueType *mq, uintptr_t data, TimeSpan timeout);
/* Receive functionality */
void ReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq);
bool TryReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq);
bool TimedReceiveMessageQueue(uintptr_t *out, MessageQueueType *mq, TimeSpan timeout);
/* Peek functionality */
void PeekMessageQueue(uintptr_t *out, const MessageQueueType *mq);
bool TryPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq);
bool TimedPeekMessageQueue(uintptr_t *out, const MessageQueueType *mq, TimeSpan timeout);
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, MessageQueueType *event, MessageQueueWaitType wait_type);
}

View file

@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
enum class MessageQueueWaitType {
ForNotFull = 1,
ForNotEmpty = 2,
};
}

View file

@ -0,0 +1,50 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
namespace ams::os {
namespace impl {
class WaitableObjectList;
}
struct MessageQueueType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitlist_not_full;
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitlist_not_empty;
uintptr_t *buffer;
s32 capacity;
s32 count;
s32 offset;
u8 state;
mutable impl::InternalCriticalSectionStorage cs_queue;
mutable impl::InternalConditionVariableStorage cv_not_full;
mutable impl::InternalConditionVariableStorage cv_not_empty;
};
static_assert(std::is_trivial<MessageQueueType>::value);
}

View file

@ -15,82 +15,60 @@
*/ */
#pragma once #pragma once
#include "os_common_types.hpp" #include <stratosphere/os/os_mutex_common.hpp>
#include <stratosphere/os/os_mutex_types.hpp>
#include <stratosphere/os/os_mutex_api.hpp>
namespace ams::os { namespace ams::os {
class ConditionVariable;
class Mutex { class Mutex {
NON_COPYABLE(Mutex); NON_COPYABLE(Mutex);
NON_MOVEABLE(Mutex); NON_MOVEABLE(Mutex);
friend class ams::os::ConditionVariable;
private: private:
::Mutex m; MutexType mutex;
private:
constexpr ::Mutex *GetMutex() {
return &this->m;
}
public: public:
constexpr Mutex() : m() { /* ... */ } constexpr explicit Mutex(bool recursive) : mutex{::ams::os::MutexType::State_Initialized, recursive, 0, 0, nullptr, {{0}}} { /* ... */ }
~Mutex() { FinalizeMutex(std::addressof(this->mutex)); }
void lock() { void lock() {
mutexLock(GetMutex()); return LockMutex(std::addressof(this->mutex));
} }
void unlock() { void unlock() {
mutexUnlock(GetMutex()); return UnlockMutex(std::addressof(this->mutex));
} }
bool try_lock() { bool try_lock() {
return mutexTryLock(GetMutex()); return TryLockMutex(std::addressof(this->mutex));
} }
void Lock() { bool IsLockedByCurrentThread() const {
lock(); return IsMutexLockedByCurrentThread(std::addressof(this->mutex));
} }
void Unlock() { ALWAYS_INLINE void Lock() {
unlock(); return this->lock();
} }
bool TryLock() { ALWAYS_INLINE void Unlock() {
return try_lock(); return this->unlock();
}
};
class RecursiveMutex {
private:
::RMutex m;
private:
constexpr ::RMutex *GetMutex() {
return &this->m;
}
public:
constexpr RecursiveMutex() : m() { /* ... */ }
void lock() {
rmutexLock(GetMutex());
} }
void unlock() { ALWAYS_INLINE bool TryLock() {
rmutexUnlock(GetMutex()); return this->try_lock();
} }
bool try_lock() { operator MutexType &() {
return rmutexTryLock(GetMutex()); return this->mutex;
} }
void Lock() { operator const MutexType &() const {
lock(); return this->mutex;
} }
void Unlock() { MutexType *GetBase() {
unlock(); return std::addressof(this->mutex);
}
bool TryLock() {
return try_lock();
} }
}; };

View file

@ -0,0 +1,34 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_mutex_common.hpp>
namespace ams::os {
struct MutexType;
void InitializeMutex(MutexType *mutex, bool recursive, int lock_level);
void FinalizeMutex(MutexType *mutex);
void LockMutex(MutexType *mutex);
bool TryLockMutex(MutexType *mutex);
void UnlockMutex(MutexType *mutex);
bool IsMutexLockedByCurrentThread(const MutexType *mutex);
}

View file

@ -0,0 +1,27 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
constexpr inline s32 MutexLockLevelMin = 1;
constexpr inline s32 MutexLockLevelMax = BITSIZEOF(s32) - 1;
constexpr inline s32 MutexLockLevelInitial = 0;
constexpr inline s32 MutexRecursiveLockCountMax = (1 << BITSIZEOF(u16)) - 1;
}

View file

@ -0,0 +1,43 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
namespace ams::os {
struct ThreadType;
struct MutexType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
u8 state;
bool is_recursive;
s32 lock_level;
s32 nest_count;
ThreadType *owner_thread;
union {
s32 _arr[sizeof(impl::InternalCriticalSectionStorage) / sizeof(s32)];
impl::InternalCriticalSectionStorage _storage;
};
};
static_assert(std::is_trivial<MutexType>::value);
}

View file

@ -13,43 +13,58 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "os_mutex.hpp" #include <stratosphere/os/os_semaphore_types.hpp>
#include "os_condvar.hpp" #include <stratosphere/os/os_semaphore_api.hpp>
namespace ams::os { namespace ams::os {
namespace impl {
class WaitableObjectList;
class WaitableHolderOfSemaphore;
}
class Semaphore { class Semaphore {
friend class impl::WaitableHolderOfSemaphore;
NON_COPYABLE(Semaphore); NON_COPYABLE(Semaphore);
NON_MOVEABLE(Semaphore); NON_MOVEABLE(Semaphore);
private: private:
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitlist; SemaphoreType sema;
os::Mutex mutex;
os::ConditionVariable condvar;
int count;
int max_count;
public: public:
explicit Semaphore(int c, int mc); explicit Semaphore(s32 count, s32 max_count) {
~Semaphore(); InitializeSemaphore(std::addressof(this->sema), count, max_count);
}
void Acquire(); ~Semaphore() { FinalizeSemaphore(std::addressof(this->sema)); }
bool TryAcquire();
bool TimedAcquire(u64 timeout);
void Release(); void Acquire() {
void Release(int count); return os::AcquireSemaphore(std::addressof(this->sema));
}
constexpr inline int GetCurrentCount() const { bool TryAcquire() {
return this->count; return os::TryAcquireSemaphore(std::addressof(this->sema));
}
bool TimedAcquire(TimeSpan timeout) {
return os::TimedAcquireSemaphore(std::addressof(this->sema), timeout);
}
void Release() {
return os::ReleaseSemaphore(std::addressof(this->sema));
}
void Release(s32 count) {
return os::ReleaseSemaphore(std::addressof(this->sema), count);
}
s32 GetCurrentCount() const {
return os::GetCurrentSemaphoreCount(std::addressof(this->sema));
}
operator SemaphoreType &() {
return this->sema;
}
operator const SemaphoreType &() const {
return this->sema;
}
SemaphoreType *GetBase() {
return std::addressof(this->sema);
} }
}; };

View file

@ -0,0 +1,39 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
struct SemaphoreType;
struct WaitableHolderType;
void InitializeSemaphore(SemaphoreType *sema, s32 count, s32 max_count);
void FinalizeSemaphore(SemaphoreType *sema);
void AcquireSemaphore(SemaphoreType *sema);
bool TryAcquireSemaphore(SemaphoreType *sema);
bool TimedAcquireSemaphore(SemaphoreType *sema, TimeSpan timeout);
void ReleaseSemaphore(SemaphoreType *sema);
void ReleaseSemaphore(SemaphoreType *sema, s32 count);
s32 GetCurrentSemaphoreCount(const SemaphoreType *sema);
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SemaphoreType *sema);
}

View file

@ -0,0 +1,46 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
namespace ams::os {
namespace impl {
class WaitableObjectList;
}
struct SemaphoreType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitlist;
u8 state;
int count;
int max_count;
impl::InternalCriticalSectionStorage cs_sema;
impl::InternalConditionVariableStorage cv_not_zero;
};
static_assert(std::is_trivial<SemaphoreType>::value);
}

View file

@ -15,66 +15,99 @@
*/ */
#pragma once #pragma once
#include "os_event.hpp" #include <stratosphere/os/os_event_common.hpp>
#include <stratosphere/os/os_system_event_types.hpp>
#include <stratosphere/os/os_system_event_api.hpp>
namespace ams::os { namespace ams::os {
class WaitableHolder;
namespace impl {
class InterProcessEvent;
}
enum class SystemEventState {
Uninitialized,
Event,
InterProcessEvent,
};
class SystemEvent { class SystemEvent {
friend class WaitableHolder;
NON_COPYABLE(SystemEvent); NON_COPYABLE(SystemEvent);
NON_MOVEABLE(SystemEvent); NON_MOVEABLE(SystemEvent);
private: private:
union { SystemEventType system_event;
util::TypedStorage<Event, sizeof(Event), alignof(Event)> storage_for_event;
util::TypedStorage<impl::InterProcessEvent, 3 * sizeof(Handle), alignof(Handle)> storage_for_inter_process_event;
};
SystemEventState state;
private:
Event &GetEvent();
const Event &GetEvent() const;
impl::InterProcessEvent &GetInterProcessEvent();
const impl::InterProcessEvent &GetInterProcessEvent() const;
public: public:
SystemEvent() : state(SystemEventState::Uninitialized) { /* ... */ } SystemEvent() {
SystemEvent(bool inter_process, bool autoclear = true); this->system_event.state = SystemEventType::State_NotInitialized;
SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
SystemEvent(Handle read_handle, bool manage_read_handle, bool autoclear = true) : SystemEvent(read_handle, manage_read_handle, INVALID_HANDLE, false, autoclear) { /* ... */ }
~SystemEvent();
Result InitializeAsEvent(bool autoclear = true);
Result InitializeAsInterProcessEvent(bool autoclear = true);
void AttachHandles(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true);
void AttachReadableHandle(Handle read_handle, bool manage_read_handle, bool autoclear = true);
void AttachWritableHandle(Handle write_handle, bool manage_write_handle, bool autoclear = true);
Handle DetachReadableHandle();
Handle DetachWritableHandle();
Handle GetReadableHandle() const;
Handle GetWritableHandle() const;
void Finalize();
SystemEventState GetState() const {
return this->state;
} }
void Signal(); explicit SystemEvent(EventClearMode clear_mode, bool inter_process) {
void Reset(); R_ABORT_UNLESS(CreateSystemEvent(std::addressof(this->system_event), clear_mode, inter_process));
void Wait(); }
bool TryWait();
bool TimedWait(u64 ns); explicit SystemEvent(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) {
AttachSystemEvent(std::addressof(this->system_event), read_handle, manage_read_handle, write_handle, manage_write_handle, clear_mode);
}
~SystemEvent() {
if (this->system_event.state == SystemEventType::State_NotInitialized) {
return;
}
DestroySystemEvent(std::addressof(this->system_event));
}
void Attach(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) {
AMS_ABORT_UNLESS(this->system_event.state == SystemEventType::State_NotInitialized);
return AttachSystemEvent(std::addressof(this->system_event), read_handle, manage_read_handle, write_handle, manage_write_handle, clear_mode);
}
void AttachReadableHandle(Handle read_handle, bool manage_read_handle, EventClearMode clear_mode) {
AMS_ABORT_UNLESS(this->system_event.state == SystemEventType::State_NotInitialized);
return AttachReadableHandleToSystemEvent(std::addressof(this->system_event), read_handle, manage_read_handle, clear_mode);
}
void AttachWritableHandle(Handle write_handle, bool manage_write_handle, EventClearMode clear_mode) {
AMS_ABORT_UNLESS(this->system_event.state == SystemEventType::State_NotInitialized);
return AttachWritableHandleToSystemEvent(std::addressof(this->system_event), write_handle, manage_write_handle, clear_mode);
}
Handle DetachReadableHandle() {
return DetachReadableHandleOfSystemEvent(std::addressof(this->system_event));
}
Handle DetachWritableHandle() {
return DetachWritableHandleOfSystemEvent(std::addressof(this->system_event));
}
void Wait() {
return WaitSystemEvent(std::addressof(this->system_event));
}
bool TryWait() {
return TryWaitSystemEvent(std::addressof(this->system_event));
}
bool TimedWait(TimeSpan timeout) {
return TimedWaitSystemEvent(std::addressof(this->system_event), timeout);
}
void Signal() {
return SignalSystemEvent(std::addressof(this->system_event));
}
void Clear() {
return ClearSystemEvent(std::addressof(this->system_event));
}
Handle GetReadableHandle() const {
return GetReadableHandleOfSystemEvent(std::addressof(this->system_event));
}
Handle GetWritableHandle() const {
return GetWritableHandleOfSystemEvent(std::addressof(this->system_event));
}
operator SystemEventType &() {
return this->system_event;
}
operator const SystemEventType &() const {
return this->system_event;
}
SystemEventType *GetBase() {
return std::addressof(this->system_event);
}
}; };
} }

View file

@ -0,0 +1,47 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_event_common.hpp>
namespace ams::os {
struct SystemEventType;
struct WaitableHolderType;
Result CreateSystemEvent(SystemEventType *event, EventClearMode clear_mode, bool inter_process);
void DestroySystemEvent(SystemEventType *event);
void AttachSystemEvent(SystemEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode);
void AttachReadableHandleToSystemEvent(SystemEventType *event, Handle read_handle, bool manage_read_handle, EventClearMode clear_mode);
void AttachWritableHandleToSystemEvent(SystemEventType *event, Handle write_handle, bool manage_write_handle, EventClearMode clear_mode);
Handle DetachReadableHandleOfSystemEvent(SystemEventType *event);
Handle DetachWritableHandleOfSystemEvent(SystemEventType *event);
Handle GetReadableHandleOfSystemEvent(const SystemEventType *event);
Handle GetWritableHandleOfSystemEvent(const SystemEventType *event);
void SignalSystemEvent(SystemEventType *event);
void WaitSystemEvent(SystemEventType *event);
bool TryWaitSystemEvent(SystemEventType *event);
bool TimedWaitSystemEvent(SystemEventType *event, TimeSpan timeout);
void ClearSystemEvent(SystemEventType *event);
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, SystemEventType *event);
}

View file

@ -0,0 +1,60 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_event_types.hpp>
namespace ams::os {
namespace impl {
struct InterProcessEventType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
};
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitable_object_list_storage;
bool auto_clear;
u8 state;
bool is_readable_handle_managed;
bool is_writable_handle_managed;
Handle readable_handle;
Handle writable_handle;
};
static_assert(std::is_trivial<InterProcessEventType>::value);
}
struct SystemEventType {
enum State {
State_NotInitialized = 0,
State_InitializedAsEvent = 1,
State_InitializedAsInterProcessEvent = 2,
};
union {
EventType event;
impl::InterProcessEventType inter_process_event;
};
u8 state;
};
static_assert(std::is_trivial<SystemEventType>::value);
}

View file

@ -13,107 +13,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "os_common_types.hpp" #include <vapours.hpp>
#include "os_memory_common.hpp" #include <stratosphere/os/os_thread_common.hpp>
#include <stratosphere/os/os_thread_types.hpp>
#include <stratosphere/os/os_thread_api.hpp>
namespace ams::os {
class Thread {
NON_COPYABLE(Thread);
NON_MOVEABLE(Thread);
private:
::Thread thr;
public:
constexpr Thread() : thr{} { /* ... */ }
Result Initialize(ThreadFunc entry, void *arg, void *stack_mem, size_t stack_sz, int prio, int cpuid = -2) {
return threadCreate(&this->thr, entry, arg, stack_mem, stack_sz, prio, cpuid);
}
Result Initialize(ThreadFunc entry, void *arg, size_t stack_sz, int prio, int cpuid = -2) {
return threadCreate(&this->thr, entry, arg, nullptr, stack_sz, prio, cpuid);
}
Handle GetHandle() const {
return this->thr.handle;
}
Result Start() {
return threadStart(&this->thr);
}
Result Wait() {
return threadWaitForExit(&this->thr);
}
Result Join() {
R_TRY(threadWaitForExit(&this->thr));
R_TRY(threadClose(&this->thr));
return ResultSuccess();
}
Result CancelSynchronization() {
return svcCancelSynchronization(this->thr.handle);
}
};
template<size_t StackSize>
class StaticThread {
NON_COPYABLE(StaticThread);
NON_MOVEABLE(StaticThread);
static_assert(util::IsAligned(StackSize, os::MemoryPageSize), "StaticThread must have aligned resource size");
private:
alignas(os::MemoryPageSize) u8 stack_mem[StackSize];
::Thread thr;
public:
constexpr StaticThread() : stack_mem{}, thr{} { /* ... */ }
constexpr StaticThread(ThreadFunc entry, void *arg, int prio, int cpuid = -2) : StaticThread() {
R_ABORT_UNLESS(this->Initialize(entry, arg, prio, cpuid));
}
Result Initialize(ThreadFunc entry, void *arg, int prio, int cpuid = -2) {
return threadCreate(&this->thr, entry, arg, this->stack_mem, StackSize, prio, cpuid);
}
Handle GetHandle() const {
return this->thr.handle;
}
Result Start() {
return threadStart(&this->thr);
}
Result Wait() {
return threadWaitForExit(&this->thr);
}
Result Join() {
R_TRY(threadWaitForExit(&this->thr));
R_TRY(threadClose(&this->thr));
return ResultSuccess();
}
Result CancelSynchronization() {
return svcCancelSynchronization(this->thr.handle);
}
};
ALWAYS_INLINE s32 GetCurrentThreadPriority() {
s32 prio;
R_ABORT_UNLESS(svcGetThreadPriority(&prio, CUR_THREAD_HANDLE));
return prio;
}
/* TODO: ThreadManager? */
ALWAYS_INLINE s32 GetCurrentProcessorNumber() {
return svcGetCurrentProcessorNumber();
}
ALWAYS_INLINE s32 GetCurrentCoreNumber() {
return GetCurrentProcessorNumber();
}
}

View file

@ -0,0 +1,67 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_thread_common.hpp>
namespace ams::os {
struct ThreadType;
struct WaitableHolderType;
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core);
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority);
void DestroyThread(ThreadType *thread);
void StartThread(ThreadType *thread);
ThreadType *GetCurrentThread();
void WaitThread(ThreadType *thread);
bool TryWaitThread(ThreadType *thread);
void YieldThread();
void SleepThread(TimeSpan time);
s32 SuspendThread(ThreadType *thread);
s32 ResumeThread(ThreadType *thread);
s32 GetThreadSuspendCount(const ThreadType *thread);
void CancelThreadSynchronization(ThreadType *Thread);
/* TODO: void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
s32 ChangeThreadPriority(ThreadType *thread, s32 priority);
s32 GetThreadPriority(const ThreadType *thread);
s32 GetThreadCurrentPriority(const ThreadType *thread);
void SetThreadName(ThreadType *thread, const char *name);
void SetThreadNamePointer(ThreadType *thread, const char *name);
const char *GetThreadNamePointer(const ThreadType *thread);
s32 GetCurrentProcessorNumber();
s32 GetCurrentCoreNumber();
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask);
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread);
u64 GetThreadAvailableCoreMask();
ThreadId GetThreadId(const ThreadType *thread);
void InitializeWaitableHolder(WaitableHolderType *holder, ThreadType *thread);
}

View file

@ -0,0 +1,38 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::os {
constexpr inline s32 ThreadSuspendCountMax = 127;
constexpr inline s32 ThreadNameLengthMax = 0x20;
constexpr inline s32 ThreadPriorityRangeSize = 32;
constexpr inline s32 HighestThreadPriority = 0;
constexpr inline s32 DefaultThreadPriority = ThreadPriorityRangeSize / 2;
constexpr inline s32 LowestThreadPriority = ThreadPriorityRangeSize - 1;
constexpr inline s32 LowestSystemThreadPriority = 35;
constexpr inline s32 HighestSystemThreadPriority = -12;
constexpr inline size_t StackGuardAlignment = 4_KB;
constexpr inline size_t ThreadStackAlignment = 4_KB;
using ThreadFunction = void (*)(void *);
}

View file

@ -0,0 +1,72 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_thread_common.hpp>
#include <stratosphere/os/os_thread_local_storage_common.hpp>
#include <stratosphere/os/os_thread_local_storage_api.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
#include <stratosphere/os/impl/os_internal_condition_variable.hpp>
namespace ams::os {
namespace impl {
class WaitableObjectList;
}
using ThreadId = u64;
/* TODO */
using ThreadImpl = ::Thread;
struct ThreadType {
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
State_DestroyedBeforeStarted = 2,
State_Started = 3,
State_Terminated = 4,
};
TYPED_STORAGE(util::IntrusiveListNode) all_threads_node;
util::TypedStorage<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> waitlist;
uintptr_t reserved[4];
u8 state;
u8 suspend_count;
s32 base_priority;
char name_buffer[ThreadNameLengthMax];
const char *name_pointer;
ThreadId thread_id;
void *stack;
size_t stack_size;
ThreadFunction function;
void *argument;
mutable impl::InternalCriticalSectionStorage cs_thread;
mutable impl::InternalConditionVariableStorage cv_thread;
ThreadImpl *thread_impl;
ThreadImpl thread_impl_storage;
};
static_assert(std::is_trivial<ThreadType>::value);
constexpr inline s32 IdealCoreDontCare = -1;
constexpr inline s32 IdealCoreUseDefault = -2;
constexpr inline s32 IdealCoreNoUpdate = -3;
}

View file

@ -1,63 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "os_common_types.hpp"
namespace ams::os {
class TimeoutHelper {
private:
u64 end_tick;
public:
TimeoutHelper(u64 ns) {
/* Special case zero-time timeouts. */
if (ns == 0) {
end_tick = 0;
return;
}
u64 cur_tick = armGetSystemTick();
this->end_tick = cur_tick + NsToTick(ns) + 1;
}
static constexpr inline u64 NsToTick(u64 ns) {
return (ns * 12) / 625;
}
static constexpr inline u64 TickToNs(u64 tick) {
return (tick * 625) / 12;
}
inline bool TimedOut() const {
if (this->end_tick == 0) {
return true;
}
return armGetSystemTick() >= this->end_tick;
}
inline u64 NsUntilTimeout() const {
u64 diff = TickToNs(this->end_tick - armGetSystemTick());
if (this->TimedOut()) {
return 0;
}
return diff;
}
};
}

View file

@ -0,0 +1,19 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/os/os_waitable_types.hpp>
#include <stratosphere/os/os_waitable_api.hpp>
#include <stratosphere/os/os_waitable_utils.hpp>

View file

@ -0,0 +1,45 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_message_queue_common.hpp>
namespace ams::os {
struct WaitableHolderType;
struct WaitableManagerType;
void InitializeWaitableManager(WaitableManagerType *manager);
void FinalizeWaitableManager(WaitableManagerType *manager);
WaitableHolderType *WaitAny(WaitableManagerType *manager);
WaitableHolderType *TryWaitAny(WaitableManagerType *manager);
WaitableHolderType *TimedWaitAny(WaitableManagerType *manager, TimeSpan timeout);
void FinalizeWaitableHolder(WaitableHolderType *holder);
void LinkWaitableHolder(WaitableManagerType *manager, WaitableHolderType *holder);
void UnlinkWaitableHolder(WaitableHolderType *holder);
void UnlinkAllWaitableHolder(WaitableManagerType *manager);
void MoveAllWaitableHolder(WaitableManagerType *dst, WaitableManagerType *src);
void SetWaitableHolderUserData(WaitableHolderType *holder, uintptr_t user_data);
uintptr_t GetWaitableHolderUserData(const WaitableHolderType *holder);
void InitializeWaitableHolder(WaitableHolderType *holder, Handle handle);
}

View file

@ -1,67 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "os_common_types.hpp"
namespace ams::os {
class WaitableManager;
class Event;
class SystemEvent;
class InterruptEvent;
class Thread;
class MessageQueue;
class Semaphore;
namespace impl {
class WaitableHolderImpl;
}
class WaitableHolder {
friend class WaitableManager;
NON_COPYABLE(WaitableHolder);
NON_MOVEABLE(WaitableHolder);
private:
util::TypedStorage<impl::WaitableHolderImpl, 2 * sizeof(util::IntrusiveListNode) + 3 * sizeof(void *), alignof(void *)> impl_storage;
uintptr_t user_data;
public:
static constexpr size_t ImplStorageSize = sizeof(impl_storage);
public:
WaitableHolder(Handle handle);
WaitableHolder(Event *event);
WaitableHolder(SystemEvent *event);
WaitableHolder(InterruptEvent *event);
WaitableHolder(Thread *thread);
WaitableHolder(Semaphore *semaphore);
WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind);
~WaitableHolder();
void SetUserData(uintptr_t data) {
this->user_data = data;
}
uintptr_t GetUserData() const {
return this->user_data;
}
void UnlinkFromWaitableManager();
};
}

View file

@ -1,51 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "os_mutex.hpp"
namespace ams::os {
class WaitableHolder;
namespace impl {
class WaitableManagerImpl;
}
class WaitableManager {
NON_COPYABLE(WaitableManager);
NON_MOVEABLE(WaitableManager);
private:
util::TypedStorage<impl::WaitableManagerImpl, sizeof(util::IntrusiveListNode) + sizeof(Mutex) + 2 * sizeof(void *) + sizeof(Handle), alignof(void *)> impl_storage;
public:
static constexpr size_t ImplStorageSize = sizeof(impl_storage);
public:
WaitableManager();
~WaitableManager();
/* Wait. */
WaitableHolder *WaitAny();
WaitableHolder *TryWaitAny();
WaitableHolder *TimedWaitAny(u64 timeout);
/* Link. */
void LinkWaitableHolder(WaitableHolder *holder);
void UnlinkAll();
void MoveAllFrom(WaitableManager *other);
};
}

View file

@ -0,0 +1,47 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/impl/os_internal_critical_section.hpp>
namespace ams::os {
namespace impl {
class WaitableManagerImpl;
struct WaitableHolderImpl;
}
struct WaitableManagerType {
enum State {
State_NotInitialized,
State_Initialized,
};
u8 state;
bool is_waiting;
util::TypedStorage<impl::WaitableManagerImpl, sizeof(util::IntrusiveListNode) + sizeof(impl::InternalCriticalSection) + 2 * sizeof(void *) + sizeof(Handle), alignof(void *)> impl_storage;
};
static_assert(std::is_trivial<WaitableManagerType>::value);
struct WaitableHolderType {
util::TypedStorage<impl::WaitableHolderImpl, 2 * sizeof(util::IntrusiveListNode) + 3 * sizeof(void *), alignof(void *)> impl_storage;
uintptr_t user_data;
};
static_assert(std::is_trivial<WaitableHolderType>::value);
}

View file

@ -0,0 +1,22 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_message_queue_common.hpp>
namespace ams::os {
}

View file

@ -109,7 +109,7 @@ namespace ams::sf::cmif {
virtual void *AllocateDomain() = 0; virtual void *AllocateDomain() = 0;
virtual void FreeDomain(void *) = 0; virtual void FreeDomain(void *) = 0;
protected: protected:
ServerDomainManager(DomainEntryStorage *entry_storage, size_t entry_count) : entry_manager(entry_storage, entry_count) { /* ... */ } ServerDomainManager(DomainEntryStorage *entry_storage, size_t entry_count) : entry_owner_lock(false), entry_manager(entry_storage, entry_count) { /* ... */ }
inline DomainServiceObject *AllocateDomainServiceObject() { inline DomainServiceObject *AllocateDomainServiceObject() {
void *storage = this->AllocateDomain(); void *storage = this->AllocateDomain();

View file

@ -28,6 +28,9 @@ namespace ams::sf::hipc {
NeedsRetry, NeedsRetry,
}; };
void AttachWaitableHolderForAccept(os::WaitableHolderType *holder, Handle port);
void AttachWaitableHolderForReply(os::WaitableHolderType *holder, Handle request);
Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer); Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer);
Result Receive(bool *out_closed, Handle session_handle, const cmif::PointerAndSize &message_buffer); Result Receive(bool *out_closed, Handle session_handle, const cmif::PointerAndSize &message_buffer);
Result Reply(Handle session_handle, const cmif::PointerAndSize &message_buffer); Result Reply(Handle session_handle, const cmif::PointerAndSize &message_buffer);

View file

@ -47,7 +47,7 @@ namespace ams::sf::hipc {
using ServerDomainSessionManager::DomainEntryStorage; using ServerDomainSessionManager::DomainEntryStorage;
using ServerDomainSessionManager::DomainStorage; using ServerDomainSessionManager::DomainStorage;
private: private:
class ServerBase : public os::WaitableHolder { class ServerBase : public os::WaitableHolderType {
friend class ServerManagerBase; friend class ServerManagerBase;
template<size_t, typename, size_t> template<size_t, typename, size_t>
friend class ServerManager; friend class ServerManager;
@ -60,9 +60,9 @@ namespace ams::sf::hipc {
bool service_managed; bool service_managed;
public: public:
ServerBase(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) : ServerBase(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) :
os::WaitableHolder(ph), static_object(std::move(sh)), port_handle(ph), service_name(sn), service_managed(m) static_object(std::move(sh)), port_handle(ph), service_name(sn), service_managed(m)
{ {
/* ... */ hipc::AttachWaitableHolderForAccept(this, ph);
} }
virtual ~ServerBase() = 0; virtual ~ServerBase() = 0;
@ -87,7 +87,7 @@ namespace ams::sf::hipc {
} else { } else {
R_ABORT_UNLESS(sm::UnregisterService(this->service_name)); R_ABORT_UNLESS(sm::UnregisterService(this->service_name));
} }
R_ABORT_UNLESS(svcCloseHandle(this->port_handle)); R_ABORT_UNLESS(svc::CloseHandle(this->port_handle));
} }
} }
@ -118,30 +118,30 @@ namespace ams::sf::hipc {
}; };
private: private:
/* Management of waitables. */ /* Management of waitables. */
os::WaitableManager waitable_manager; os::WaitableManagerType waitable_manager;
os::Event request_stop_event; os::Event request_stop_event;
os::WaitableHolder request_stop_event_holder; os::WaitableHolderType request_stop_event_holder;
os::Event notify_event; os::Event notify_event;
os::WaitableHolder notify_event_holder; os::WaitableHolderType notify_event_holder;
os::Mutex waitable_selection_mutex; os::Mutex waitable_selection_mutex;
os::Mutex waitlist_mutex; os::Mutex waitlist_mutex;
os::WaitableManager waitlist; os::WaitableManagerType waitlist;
os::Mutex deferred_session_mutex; os::Mutex deferred_session_mutex;
using DeferredSessionList = typename util::IntrusiveListMemberTraits<&ServerSession::deferred_list_node>::ListType; using DeferredSessionList = typename util::IntrusiveListMemberTraits<&ServerSession::deferred_list_node>::ListType;
DeferredSessionList deferred_session_list; DeferredSessionList deferred_session_list;
private: private:
virtual void RegisterSessionToWaitList(ServerSession *session) override final; virtual void RegisterSessionToWaitList(ServerSession *session) override final;
void RegisterToWaitList(os::WaitableHolder *holder); void RegisterToWaitList(os::WaitableHolderType *holder);
void ProcessWaitList(); void ProcessWaitList();
bool WaitAndProcessImpl(); bool WaitAndProcessImpl();
Result ProcessForServer(os::WaitableHolder *holder); Result ProcessForServer(os::WaitableHolderType *holder);
Result ProcessForMitmServer(os::WaitableHolder *holder); Result ProcessForMitmServer(os::WaitableHolderType *holder);
Result ProcessForSession(os::WaitableHolder *holder); Result ProcessForSession(os::WaitableHolderType *holder);
void ProcessDeferredSessions(); void ProcessDeferredSessions();
@ -154,13 +154,13 @@ namespace ams::sf::hipc {
if constexpr (!ServiceObjectTraits<ServiceImpl>::IsMitmServiceObject) { if constexpr (!ServiceObjectTraits<ServiceImpl>::IsMitmServiceObject) {
/* Non-mitm server. */ /* Non-mitm server. */
server->SetUserData(static_cast<uintptr_t>(UserDataTag::Server)); os::SetWaitableHolderUserData(server, static_cast<uintptr_t>(UserDataTag::Server));
} else { } else {
/* Mitm server. */ /* Mitm server. */
server->SetUserData(static_cast<uintptr_t>(UserDataTag::MitmServer)); os::SetWaitableHolderUserData(server, static_cast<uintptr_t>(UserDataTag::MitmServer));
} }
this->waitable_manager.LinkWaitableHolder(server); os::LinkWaitableHolder(std::addressof(this->waitable_manager), server);
} }
template<typename ServiceImpl> template<typename ServiceImpl>
@ -175,12 +175,16 @@ namespace ams::sf::hipc {
public: public:
ServerManagerBase(DomainEntryStorage *entry_storage, size_t entry_count) : ServerManagerBase(DomainEntryStorage *entry_storage, size_t entry_count) :
ServerDomainSessionManager(entry_storage, entry_count), ServerDomainSessionManager(entry_storage, entry_count),
request_stop_event(false), request_stop_event_holder(&request_stop_event), request_stop_event(os::EventClearMode_ManualClear), notify_event(os::EventClearMode_ManualClear),
notify_event(false), notify_event_holder(&notify_event) waitable_selection_mutex(false), waitlist_mutex(false), deferred_session_mutex(false)
{ {
/* Link waitables. */ /* Link waitables. */
this->waitable_manager.LinkWaitableHolder(&this->request_stop_event_holder); os::InitializeWaitableManager(std::addressof(this->waitable_manager));
this->waitable_manager.LinkWaitableHolder(&this->notify_event_holder); os::InitializeWaitableHolder(std::addressof(this->request_stop_event_holder), this->request_stop_event.GetBase());
os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->request_stop_event_holder));
os::InitializeWaitableHolder(std::addressof(this->notify_event_holder), this->notify_event.GetBase());
os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->notify_event_holder));
os::InitializeWaitableManager(std::addressof(this->waitlist));
} }
template<typename ServiceImpl, auto MakeShared = std::make_shared<ServiceImpl>> template<typename ServiceImpl, auto MakeShared = std::make_shared<ServiceImpl>>
@ -224,13 +228,13 @@ namespace ams::sf::hipc {
} }
/* Processing. */ /* Processing. */
os::WaitableHolder *WaitSignaled(); os::WaitableHolderType *WaitSignaled();
void ResumeProcessing(); void ResumeProcessing();
void RequestStopProcessing(); void RequestStopProcessing();
void AddUserWaitableHolder(os::WaitableHolder *waitable); void AddUserWaitableHolder(os::WaitableHolderType *waitable);
Result Process(os::WaitableHolder *waitable); Result Process(os::WaitableHolderType *waitable);
void WaitAndProcess(); void WaitAndProcess();
void LoopProcess(); void LoopProcess();
}; };
@ -356,7 +360,7 @@ namespace ams::sf::hipc {
return this->GetObjectBySessionIndex(session, this->saved_messages_start, hipc::TlsMessageBufferSize); return this->GetObjectBySessionIndex(session, this->saved_messages_start, hipc::TlsMessageBufferSize);
} }
public: public:
ServerManager() : ServerManagerBase(this->domain_entry_storages, ManagerOptions::MaxDomainObjects) { ServerManager() : ServerManagerBase(this->domain_entry_storages, ManagerOptions::MaxDomainObjects), resource_mutex(false) {
/* Clear storages. */ /* Clear storages. */
#define SF_SM_MEMCLEAR(obj) if constexpr (sizeof(obj) > 0) { std::memset(obj, 0, sizeof(obj)); } #define SF_SM_MEMCLEAR(obj) if constexpr (sizeof(obj) > 0) { std::memset(obj, 0, sizeof(obj)); }
SF_SM_MEMCLEAR(this->server_storages); SF_SM_MEMCLEAR(this->server_storages);

View file

@ -38,7 +38,7 @@ namespace ams::sf::hipc {
} }
class ServerSession : public os::WaitableHolder { class ServerSession : public os::WaitableHolderType {
friend class ServerSessionManager; friend class ServerSessionManager;
friend class ServerManagerBase; friend class ServerManagerBase;
friend class impl::HipcManager; friend class impl::HipcManager;
@ -54,14 +54,16 @@ namespace ams::sf::hipc {
bool is_closed; bool is_closed;
bool has_received; bool has_received;
public: public:
ServerSession(Handle h, cmif::ServiceObjectHolder &&obj) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) { ServerSession(Handle h, cmif::ServiceObjectHolder &&obj) : srv_obj_holder(std::move(obj)), session_handle(h) {
hipc::AttachWaitableHolderForReply(this, h);
this->is_closed = false; this->is_closed = false;
this->has_received = false; this->has_received = false;
this->forward_service = nullptr; this->forward_service = nullptr;
AMS_ABORT_UNLESS(!this->IsMitmSession()); AMS_ABORT_UNLESS(!this->IsMitmSession());
} }
ServerSession(Handle h, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) { ServerSession(Handle h, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) : srv_obj_holder(std::move(obj)), session_handle(h) {
hipc::AttachWaitableHolderForReply(this, h);
this->is_closed = false; this->is_closed = false;
this->has_received = false; this->has_received = false;
this->forward_service = std::move(fsrv); this->forward_service = std::move(fsrv);

View file

@ -58,7 +58,7 @@ namespace ams::emummc {
}; };
/* Globals. */ /* Globals. */
os::Mutex g_lock; os::Mutex g_lock(false);
ExosphereConfig g_exo_config; ExosphereConfig g_exo_config;
bool g_is_emummc; bool g_is_emummc;
bool g_has_cached; bool g_has_cached;

View file

@ -25,7 +25,7 @@ namespace ams::cfg {
constexpr os::ProcessId InitialProcessIdMaxDeprecated = {0x50}; constexpr os::ProcessId InitialProcessIdMaxDeprecated = {0x50};
/* Privileged process globals. */ /* Privileged process globals. */
os::Mutex g_lock; os::Mutex g_lock(false);
bool g_got_privileged_process_status = false; bool g_got_privileged_process_status = false;
os::ProcessId g_min_initial_process_id = os::InvalidProcessId, g_max_initial_process_id = os::InvalidProcessId; os::ProcessId g_min_initial_process_id = os::InvalidProcessId, g_max_initial_process_id = os::InvalidProcessId;
os::ProcessId g_cur_process_id = os::InvalidProcessId; os::ProcessId g_cur_process_id = os::InvalidProcessId;

View file

@ -30,7 +30,7 @@ namespace ams::cfg {
constexpr size_t NumRequiredServicesForSdCardAccess = util::size(RequiredServicesForSdCardAccess); constexpr size_t NumRequiredServicesForSdCardAccess = util::size(RequiredServicesForSdCardAccess);
/* SD card globals. */ /* SD card globals. */
os::Mutex g_sd_card_lock; os::Mutex g_sd_card_lock(false);
bool g_sd_card_initialized = false; bool g_sd_card_initialized = false;
FsFileSystem g_sd_card_filesystem = {}; FsFileSystem g_sd_card_filesystem = {};

View file

@ -30,7 +30,7 @@ namespace ams::fs {
std::free(ptr); std::free(ptr);
} }
os::Mutex g_lock; os::Mutex g_lock(false);
AllocateFunction g_allocate_func = DefaultAllocate; AllocateFunction g_allocate_func = DefaultAllocate;
DeallocateFunction g_deallocate_func = DefaultDeallocate; DeallocateFunction g_deallocate_func = DefaultDeallocate;

View file

@ -60,7 +60,7 @@ namespace ams::fs::impl {
} }
FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator) FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator)
: impl(std::move(fs)), mount_name_generator(std::move(generator)), : impl(std::move(fs)), open_list_lock(false), mount_name_generator(std::move(generator)),
access_log_enabled(false), data_cache_attachable(false), path_cache_attachable(false), path_cache_attached(false), multi_commit_supported(false) access_log_enabled(false), data_cache_attachable(false), path_cache_attachable(false), path_cache_attached(false), multi_commit_supported(false)
{ {
R_ABORT_UNLESS(ValidateMountName(n)); R_ABORT_UNLESS(ValidateMountName(n));

View file

@ -28,7 +28,7 @@ namespace ams::fs::impl {
FileSystemList fs_list; FileSystemList fs_list;
os::Mutex mutex; os::Mutex mutex;
public: public:
constexpr MountTable() : fs_list(), mutex() { /* ... */ } constexpr MountTable() : fs_list(), mutex(false) { /* ... */ }
private: private:
bool CanAcceptMountName(const char *name); bool CanAcceptMountName(const char *name);
public: public:

View file

@ -17,7 +17,7 @@
namespace ams::fssystem { namespace ams::fssystem {
AesXtsStorage::AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : base_storage(base), block_size(block_size), mutex() { AesXtsStorage::AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : base_storage(base), block_size(block_size), mutex(false) {
AMS_ASSERT(base != nullptr); AMS_ASSERT(base != nullptr);
AMS_ASSERT(key1 != nullptr); AMS_ASSERT(key1 != nullptr);
AMS_ASSERT(key2 != nullptr); AMS_ASSERT(key2 != nullptr);

View file

@ -74,13 +74,13 @@ namespace ams::fssystem {
} }
DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs) DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs)
: PathResolutionFileSystem(fs), open_writable_files(0) : PathResolutionFileSystem(fs), accessor_mutex(false), open_writable_files(0)
{ {
/* ... */ /* ... */
} }
DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::unique_ptr<fs::fsa::IFileSystem> fs) DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::unique_ptr<fs::fsa::IFileSystem> fs)
: PathResolutionFileSystem(std::move(fs)), open_writable_files(0) : PathResolutionFileSystem(std::move(fs)), accessor_mutex(false), open_writable_files(0)
{ {
/* ... */ /* ... */
} }

View file

@ -27,7 +27,7 @@ namespace ams::fssystem {
uintptr_t address; uintptr_t address;
size_t size; size_t size;
public: public:
constexpr AdditionalDeviceAddressEntry() : mutex(), is_registered(), address(), size() { /* ... */ } constexpr AdditionalDeviceAddressEntry() : mutex(false), is_registered(), address(), size() { /* ... */ }
void Register(uintptr_t addr, size_t sz) { void Register(uintptr_t addr, size_t sz) {
std::scoped_lock lk(this->mutex); std::scoped_lock lk(this->mutex);
@ -78,7 +78,7 @@ namespace ams::fssystem {
constexpr size_t HeapAllocatableSizeMaxForLarge = HeapBlockSize * (static_cast<size_t>(1) << HeapOrderMaxForLarge); constexpr size_t HeapAllocatableSizeMaxForLarge = HeapBlockSize * (static_cast<size_t>(1) << HeapOrderMaxForLarge);
/* TODO: SdkMutex */ /* TODO: SdkMutex */
os::Mutex g_heap_mutex; os::Mutex g_heap_mutex(false);
FileSystemBuddyHeap g_heap; FileSystemBuddyHeap g_heap;
std::atomic<size_t> g_retry_count; std::atomic<size_t> g_retry_count;

View file

@ -20,7 +20,7 @@ namespace ams::hid {
namespace { namespace {
/* Global lock. */ /* Global lock. */
os::Mutex g_hid_lock; os::Mutex g_hid_lock(false);
bool g_initialized_hid = false; bool g_initialized_hid = false;
/* Helper. */ /* Helper. */

View file

@ -22,7 +22,7 @@ namespace ams::hos {
hos::Version g_hos_version; hos::Version g_hos_version;
bool g_has_cached; bool g_has_cached;
os::Mutex g_mutex; os::Mutex g_mutex(false);
void CacheValues() { void CacheValues() {
if (__atomic_load_n(&g_has_cached, __ATOMIC_SEQ_CST)) { if (__atomic_load_n(&g_has_cached, __ATOMIC_SEQ_CST)) {

View file

@ -33,12 +33,6 @@ namespace ams::lmem::impl {
new (&out->list_node) util::IntrusiveListNode; new (&out->list_node) util::IntrusiveListNode;
new (&out->child_list) decltype(out->child_list); new (&out->child_list) decltype(out->child_list);
/* Only initialize mutex if option requires it. */
if (option & CreateOption_ThreadSafe) {
static_assert(std::is_trivially_destructible<os::Mutex>::value);
new (&out->mutex) os::Mutex;
}
/* Set fields. */ /* Set fields. */
out->magic = magic; out->magic = magic;
out->heap_start = start; out->heap_start = start;

View file

@ -30,13 +30,13 @@ namespace ams::lmem::impl {
public: public:
explicit ScopedHeapLock(HeapHandle h) : handle(h) { explicit ScopedHeapLock(HeapHandle h) : handle(h) {
if (this->handle->option & CreateOption_ThreadSafe) { if (this->handle->option & CreateOption_ThreadSafe) {
this->handle->mutex.Lock(); os::LockMutex(std::addressof(this->handle->mutex));
} }
} }
~ScopedHeapLock() { ~ScopedHeapLock() {
if (this->handle->option & CreateOption_ThreadSafe) { if (this->handle->option & CreateOption_ThreadSafe) {
this->handle->mutex.Unlock(); os::UnlockMutex(std::addressof(this->handle->mutex));
} }
} }
}; };

View file

@ -19,10 +19,17 @@
namespace ams::lmem { namespace ams::lmem {
HeapHandle CreateExpHeap(void *address, size_t size, u32 option) { HeapHandle CreateExpHeap(void *address, size_t size, u32 option) {
return impl::CreateExpHeap(address, size, option); HeapHandle handle = impl::CreateExpHeap(address, size, option);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
} }
void DestroyExpHeap(HeapHandle handle) { void DestroyExpHeap(HeapHandle handle) {
if (handle->option & CreateOption_ThreadSafe) {
os::FinalizeMutex(std::addressof(handle->mutex));
}
impl::DestroyExpHeap(handle); impl::DestroyExpHeap(handle);
} }

View file

@ -19,18 +19,33 @@
namespace ams::lmem { namespace ams::lmem {
HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option) { HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option) {
return impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast<u16>(option), InfoPlacement_Head, nullptr); HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, DefaultAlignment, static_cast<u16>(option), InfoPlacement_Head, nullptr);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
} }
HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement) { HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, InfoPlacement info_placement) {
return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), info_placement, nullptr); HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), info_placement, nullptr);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
} }
HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head) { HeapHandle CreateUnitHeap(void *address, size_t size, size_t unit_size, u32 option, s32 alignment, HeapCommonHead *heap_head) {
return impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), InfoPlacement_Head, heap_head); HeapHandle handle = impl::CreateUnitHeap(address, size, unit_size, alignment, static_cast<u16>(option), InfoPlacement_Head, heap_head);
if (option & CreateOption_ThreadSafe) {
os::InitializeMutex(std::addressof(handle->mutex), false, 0);
}
return handle;
} }
void DestroyUnitHeap(HeapHandle handle) { void DestroyUnitHeap(HeapHandle handle) {
if (handle->option & CreateOption_ThreadSafe) {
os::FinalizeMutex(std::addressof(handle->mutex));
}
impl::DestroyUnitHeap(handle); impl::DestroyUnitHeap(handle);
} }

View file

@ -201,14 +201,14 @@ namespace ams::mem::impl::heap {
s32 static_thread_quota; s32 static_thread_quota;
s32 dynamic_thread_quota; s32 dynamic_thread_quota;
bool use_virtual_memory; bool use_virtual_memory;
os::RecursiveMutex lock; os::Mutex lock;
ListHeader<SpanPage> spanpage_list; ListHeader<SpanPage> spanpage_list;
ListHeader<SpanPage> full_spanpage_list; ListHeader<SpanPage> full_spanpage_list;
ListHeader<Span> freelists[FreeListCount]; ListHeader<Span> freelists[FreeListCount];
FreeListAvailableWord freelists_bitmap[NumFreeListBitmaps]; FreeListAvailableWord freelists_bitmap[NumFreeListBitmaps];
ListHeader<Span> smallmem_lists[TlsHeapStatic::NumClassInfo]; ListHeader<Span> smallmem_lists[TlsHeapStatic::NumClassInfo];
public: public:
TlsHeapCentral() { TlsHeapCentral() : lock(true) {
this->span_table.total_pages = 0; this->span_table.total_pages = 0;
} }

View file

@ -20,7 +20,7 @@ namespace ams::mem::impl {
namespace { namespace {
os::Mutex g_virt_mem_enabled_lock; os::Mutex g_virt_mem_enabled_lock(false);
bool g_virt_mem_enabled_detected; bool g_virt_mem_enabled_detected;
bool g_virt_mem_enabled; bool g_virt_mem_enabled;

View file

@ -626,7 +626,6 @@ namespace ams::ncm {
} }
Result InstallTaskBase::PreparePlaceHolder() { Result InstallTaskBase::PreparePlaceHolder() {
static os::Mutex placeholder_mutex;
size_t total_size = 0; size_t total_size = 0;
/* Count the number of content meta entries. */ /* Count the number of content meta entries. */
@ -635,7 +634,9 @@ namespace ams::ncm {
for (s32 i = 0; i < count; i++) { for (s32 i = 0; i < count; i++) {
R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled()); R_UNLESS(!this->IsCancelRequested(), ncm::ResultCreatePlaceHolderCancelled());
std::scoped_lock lk(placeholder_mutex);
static os::Mutex s_placeholder_mutex(false);
std::scoped_lock lk(s_placeholder_mutex);
InstallContentMeta content_meta; InstallContentMeta content_meta;
R_TRY(this->data->Get(std::addressof(content_meta), i)); R_TRY(this->data->Get(std::addressof(content_meta), i));

View file

@ -43,7 +43,7 @@ namespace ams::ncm {
CacheEntry *FindInCache(PlaceHolderId placeholder_id); CacheEntry *FindInCache(PlaceHolderId placeholder_id);
CacheEntry *GetFreeEntry();; CacheEntry *GetFreeEntry();;
public: public:
PlaceHolderAccessor() : cur_counter(0), delay_flush(false) { PlaceHolderAccessor() : cur_counter(0), cache_mutex(false), delay_flush(false) {
for (size_t i = 0; i < MaxCacheEntries; i++) { for (size_t i = 0; i < MaxCacheEntries; i++) {
caches[i].id = InvalidPlaceHolderId; caches[i].id = InvalidPlaceHolderId;
} }

View file

@ -13,167 +13,139 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stratosphere.hpp>
#include "os_inter_process_event.hpp" #include "os_inter_process_event.hpp"
#include "os_inter_process_event_impl.hpp"
#include "os_waitable_object_list.hpp"
namespace ams::os::impl { namespace ams::os::impl {
namespace { namespace {
Result CreateEventHandles(Handle *out_readable, Handle *out_writable) { inline void SetupInterProcessEventType(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
/* Create the event handles. */ /* Set handles. */
R_TRY_CATCH(svcCreateEvent(out_writable, out_readable)) { event->readable_handle = read_handle;
R_CONVERT(svc::ResultOutOfResource, ResultOutOfResource()); event->is_readable_handle_managed = read_handle_managed;
} R_END_TRY_CATCH_WITH_ABORT_UNLESS; event->writable_handle = write_handle;
event->is_writable_handle_managed = write_handle_managed;
return ResultSuccess(); /* Set auto clear. */
event->auto_clear = (clear_mode == EventClearMode_AutoClear);
/* Create the waitlist node. */
new (GetPointer(event->waitable_object_list_storage)) impl::WaitableObjectList;
/* Set state. */
event->state = InterProcessEventType::State_Initialized;
} }
} }
InterProcessEvent::InterProcessEvent(bool autoclear) : is_initialized(false) { Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode) {
R_ABORT_UNLESS(this->Initialize(autoclear));
}
InterProcessEvent::~InterProcessEvent() {
this->Finalize();
}
Result InterProcessEvent::Initialize(bool autoclear) {
AMS_ABORT_UNLESS(!this->is_initialized);
Handle rh, wh; Handle rh, wh;
R_TRY(CreateEventHandles(&rh, &wh)); R_TRY(impl::InterProcessEventImpl::Create(std::addressof(wh), std::addressof(rh)));
this->Initialize(rh, true, wh, true, autoclear);
SetupInterProcessEventType(event, rh, true, wh, true, clear_mode);
return ResultSuccess(); return ResultSuccess();
} }
void InterProcessEvent::Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear) { void DestroyInterProcessEvent(InterProcessEventType *event) {
AMS_ABORT_UNLESS(!this->is_initialized); AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
AMS_ABORT_UNLESS(read_handle != INVALID_HANDLE || write_handle != INVALID_HANDLE);
this->read_handle = read_handle; /* Clear the state. */
this->manage_read_handle = manage_read_handle; event->state = InterProcessEventType::State_NotInitialized;
this->write_handle = write_handle;
this->manage_write_handle = manage_write_handle; /* Close handles if required. */
this->auto_clear = autoclear; if (event->is_readable_handle_managed) {
this->is_initialized = true; if (event->readable_handle != svc::InvalidHandle) {
impl::InterProcessEventImpl::Close(event->readable_handle);
}
event->is_readable_handle_managed = false;
} }
Handle InterProcessEvent::DetachReadableHandle() { if (event->is_writable_handle_managed) {
AMS_ABORT_UNLESS(this->is_initialized); if (event->writable_handle != svc::InvalidHandle) {
const Handle handle = this->read_handle; impl::InterProcessEventImpl::Close(event->writable_handle);
AMS_ABORT_UNLESS(handle != INVALID_HANDLE); }
this->read_handle = INVALID_HANDLE; event->is_writable_handle_managed = false;
this->manage_read_handle = false; }
/* Destroy the waitlist. */
GetReference(event->waitable_object_list_storage).~WaitableObjectList();
}
void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode) {
AMS_ASSERT(read_handle != svc::InvalidHandle || write_handle != svc::InvalidHandle);
return SetupInterProcessEventType(event, read_handle, read_handle_managed, write_handle, write_handle_managed, clear_mode);
}
Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
const Handle handle = event->readable_handle;
event->readable_handle = svc::InvalidHandle;
event->is_readable_handle_managed = false;
return handle;
}
Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event) {
AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
const Handle handle = event->writable_handle;
event->writable_handle = svc::InvalidHandle;
event->is_writable_handle_managed = false;
return handle; return handle;
} }
Handle InterProcessEvent::DetachWritableHandle() { void WaitInterProcessEvent(InterProcessEventType *event) {
AMS_ABORT_UNLESS(this->is_initialized); AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
const Handle handle = this->write_handle;
AMS_ABORT_UNLESS(handle != INVALID_HANDLE); return impl::InterProcessEventImpl::Wait(event->readable_handle, event->auto_clear);
this->write_handle = INVALID_HANDLE;
this->manage_write_handle = false;
return handle;
} }
Handle InterProcessEvent::GetReadableHandle() const { bool TryWaitInterProcessEvent(InterProcessEventType *event) {
AMS_ABORT_UNLESS(this->is_initialized); AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
return this->read_handle;
return impl::InterProcessEventImpl::TryWait(event->readable_handle, event->auto_clear);
} }
Handle InterProcessEvent::GetWritableHandle() const { bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout) {
AMS_ABORT_UNLESS(this->is_initialized); AMS_ASSERT(event->state == InterProcessEventType::State_Initialized);
return this->write_handle; AMS_ASSERT(timeout.GetNanoSeconds() >= 0);
return impl::InterProcessEventImpl::TimedWait(event->readable_handle, event->auto_clear, timeout);
} }
void InterProcessEvent::Finalize() { void SignalInterProcessEvent(InterProcessEventType *event) {
if (this->is_initialized) { AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
if (this->manage_read_handle && this->read_handle != INVALID_HANDLE) {
R_ABORT_UNLESS(svcCloseHandle(this->read_handle)); return impl::InterProcessEventImpl::Signal(event->writable_handle);
}
if (this->manage_write_handle && this->write_handle != INVALID_HANDLE) {
R_ABORT_UNLESS(svcCloseHandle(this->write_handle));
}
}
this->read_handle = INVALID_HANDLE;
this->manage_read_handle = false;
this->write_handle = INVALID_HANDLE;
this->manage_write_handle = false;
this->is_initialized = false;
} }
void InterProcessEvent::Signal() { void ClearInterProcessEvent(InterProcessEventType *event) {
R_ABORT_UNLESS(svcSignalEvent(this->GetWritableHandle())); AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
auto handle = event->readable_handle;
if (handle == svc::InvalidHandle) {
handle = event->writable_handle;
}
return impl::InterProcessEventImpl::Clear(handle);
} }
void InterProcessEvent::Reset() { Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event) {
Handle handle = this->GetReadableHandle(); AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
if (handle == INVALID_HANDLE) {
handle = this->GetWritableHandle(); return event->readable_handle;
}
R_ABORT_UNLESS(svcClearEvent(handle));
} }
void InterProcessEvent::Wait() { Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event) {
const Handle handle = this->GetReadableHandle(); AMS_ASSERT(event->state != InterProcessEventType::State_NotInitialized);
while (true) { return event->writable_handle;
/* Continuously wait, until success. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, std::numeric_limits<u64>::max())) {
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
} }
bool InterProcessEvent::TryWait() {
const Handle handle = this->GetReadableHandle();
if (this->auto_clear) {
/* Auto-clear. Just try to reset. */
return R_SUCCEEDED(svcResetSignal(handle));
} else {
/* Not auto-clear. */
while (true) {
/* Continuously wait, until success or timeout. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, 0)) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* We succeeded, so we're signaled. */
return true;
}
}
}
bool InterProcessEvent::TimedWait(u64 ns) {
const Handle handle = this->GetReadableHandle();
TimeoutHelper timeout_helper(ns);
while (true) {
/* Continuously wait, until success or timeout. */
R_TRY_CATCH(svcWaitSynchronizationSingle(handle, timeout_helper.NsUntilTimeout())) {
R_CATCH(svc::ResultTimedOut) { return false; }
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear, if we must. */
if (this->auto_clear) {
R_TRY_CATCH(svcResetSignal(handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
}
}
} }

View file

@ -18,37 +18,24 @@
namespace ams::os::impl { namespace ams::os::impl {
class WaitableHolderOfInterProcessEvent; Result CreateInterProcessEvent(InterProcessEventType *event, EventClearMode clear_mode);
void DestroyInterProcessEvent(InterProcessEventType *event);
class InterProcessEvent { void AttachInterProcessEvent(InterProcessEventType *event, Handle read_handle, bool read_handle_managed, Handle write_handle, bool write_handle_managed, EventClearMode clear_mode);
friend class WaitableHolderOfInterProcessEvent;
NON_COPYABLE(InterProcessEvent);
NON_MOVEABLE(InterProcessEvent);
private:
Handle read_handle;
Handle write_handle;
bool manage_read_handle;
bool manage_write_handle;
bool auto_clear;
bool is_initialized;
public:
InterProcessEvent() : is_initialized(false) { /* ... */ }
InterProcessEvent(bool autoclear);
~InterProcessEvent();
Result Initialize(bool autoclear = true); Handle DetachReadableHandleOfInterProcessEvent(InterProcessEventType *event);
void Initialize(Handle read_handle, bool manage_read_handle, Handle write_handle, bool manage_write_handle, bool autoclear = true); Handle DetachWritableHandleOfInterProcessEvent(InterProcessEventType *event);
Handle DetachReadableHandle();
Handle DetachWritableHandle();
Handle GetReadableHandle() const;
Handle GetWritableHandle() const;
void Finalize();
void Signal(); void WaitInterProcessEvent(InterProcessEventType *event);
void Reset(); bool TryWaitInterProcessEvent(InterProcessEventType *event);
void Wait(); bool TimedWaitInterProcessEvent(InterProcessEventType *event, TimeSpan timeout);
bool TryWait();
bool TimedWait(u64 ns); void SignalInterProcessEvent(InterProcessEventType *event);
}; void ClearInterProcessEvent(InterProcessEventType *event);
Handle GetReadableHandleOfInterProcessEvent(const InterProcessEventType *event);
Handle GetWritableHandleOfInterProcessEvent(const InterProcessEventType *event);
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterProcessEventType *event);
} }

View file

@ -0,0 +1,23 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_inter_process_event_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::os::InterProcessEventImpl"
#endif

View file

@ -0,0 +1,123 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_inter_process_event.hpp"
#include "os_inter_process_event_impl.os.horizon.hpp"
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
Result InterProcessEventImpl::Create(Handle *out_write, Handle *out_read) {
/* Create the event handles. */
svc::Handle wh, rh;
R_TRY_CATCH(svc::CreateEvent(std::addressof(wh), std::addressof(rh))) {
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
*out_write = wh;
*out_read = rh;
return ResultSuccess();
}
void InterProcessEventImpl::Close(Handle handle) {
if (handle != svc::InvalidHandle) {
R_ABORT_UNLESS(svc::CloseHandle(svc::Handle(handle)));
}
}
void InterProcessEventImpl::Signal(Handle handle) {
R_ABORT_UNLESS(svc::SignalEvent(svc::Handle(handle)));
}
void InterProcessEventImpl::Clear(Handle handle) {
R_ABORT_UNLESS(svc::ClearEvent(svc::Handle(handle)));
}
void InterProcessEventImpl::Wait(Handle handle, bool auto_clear) {
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, svc::WaitInfinite);
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (auto_clear) {
R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterProcessEventImpl::TryWait(Handle handle, bool auto_clear) {
/* If we're auto clear, just try to reset. */
if (auto_clear) {
return R_SUCCEEDED(svc::ResetSignal(svc::Handle(handle)));
}
/* Not auto-clear. */
while (true) {
/* Continuously wait, until success or timeout. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, 0);
/* If we succeeded, we're signaled. */
if (R_SUCCEEDED(res)) {
return true;
}
/* If we timed out, we're not signaled. */
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterProcessEventImpl::TimedWait(Handle handle, bool auto_clear, TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), reinterpret_cast<svc::Handle *>(std::addressof(handle)), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (auto_clear) {
R_TRY_CATCH(svc::ResetSignal(svc::Handle(handle))) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
}
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
}

View file

@ -0,0 +1,32 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class InterProcessEventImpl {
public:
static Result Create(Handle *out_write, Handle *out_read);
static void Close(Handle handle);
static void Signal(Handle handle);
static void Clear(Handle handle);
static void Wait(Handle handle, bool auto_clear);
static bool TryWait(Handle handle, bool auto_clear);
static bool TimedWait(Handle handle, bool auto_clear, TimeSpan timeout);
};
}

View file

@ -0,0 +1,56 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
void InternalConditionVariableImpl::Signal() {
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(this->value)), 1);
}
void InternalConditionVariableImpl::Broadcast() {
ams::svc::SignalProcessWideKey(reinterpret_cast<uintptr_t>(std::addressof(this->value)), -1);
}
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
const auto cur_handle = GetCurrentThreadHandle();
AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
R_ABORT_UNLESS(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(this->value)), cur_handle, -1));
}
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
const auto cur_handle = GetCurrentThreadHandle();
AMS_ASSERT((cs->Get()->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle);
const TimeSpan left = timeout_helper.GetTimeLeftOnTarget();
if (left > 0) {
R_TRY_CATCH(ams::svc::WaitProcessWideKeyAtomic(reinterpret_cast<uintptr_t>(std::addressof(cs->Get()->thread_handle)), reinterpret_cast<uintptr_t>(std::addressof(this->value)), cur_handle, left.GetNanoSeconds())) {
R_CATCH(svc::ResultTimedOut) {
cs->Enter();
return ConditionVariableStatus::TimedOut;
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ConditionVariableStatus::Success;
} else {
return ConditionVariableStatus::TimedOut;
}
}
}

View file

@ -0,0 +1,160 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
namespace {
ALWAYS_INLINE void DataMemoryBarrierForCriticalSection() {
#if defined(ATMOSPHERE_ARCH_ARM64)
/* ... */
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl DataMemoryBarrier"
#endif
}
ALWAYS_INLINE u32 LoadExclusive(u32 *ptr) {
u32 value;
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("ldaxr %w[value], [%[ptr]]" : [value]"=&r"(value) : [ptr]"r"(ptr) : "memory");
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl LoadExclusive"
#endif
return value;
}
ALWAYS_INLINE int StoreExclusive(u32 *ptr, u32 value) {
int result;
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("stlxr %w[result], %w[value], [%[ptr]]" : [result]"=&r"(result) : [value]"r"(value), [ptr]"r"(ptr) : "memory");
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl StoreExclusive"
#endif
return result;
}
ALWAYS_INLINE void ClearExclusive() {
#if defined(ATMOSPHERE_ARCH_ARM64)
__asm__ __volatile__("clrex" ::: "memory");
#else
#error "Unknown architecture for os::impl::InternalCriticalSectionImpl ClearExclusive"
#endif
}
}
void InternalCriticalSectionImpl::Enter() {
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
const auto cur_handle = GetCurrentThreadHandle();
AMS_ASSERT((this->thread_handle & ~ams::svc::HandleWaitMask) != cur_handle);
u32 value = LoadExclusive(std::addressof(this->thread_handle));
while (true) {
if (AMS_LIKELY(value == svc::InvalidHandle)) {
if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) != 0)) {
value = LoadExclusive(std::addressof(this->thread_handle));
continue;
}
break;
}
if (AMS_LIKELY((value & ams::svc::HandleWaitMask) == 0)) {
if (AMS_UNLIKELY(StoreExclusive(std::addressof(this->thread_handle), value | ams::svc::HandleWaitMask) != 0)) {
value = LoadExclusive(std::addressof(this->thread_handle));
continue;
}
}
R_ABORT_UNLESS(ams::svc::ArbitrateLock(value & ~ams::svc::HandleWaitMask, reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle)), cur_handle));
value = LoadExclusive(std::addressof(this->thread_handle));
if (AMS_LIKELY((value & ~ams::svc::HandleWaitMask) == cur_handle)) {
ClearExclusive();
break;
}
}
DataMemoryBarrierForCriticalSection();
}
bool InternalCriticalSectionImpl::TryEnter() {
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
const auto cur_handle = GetCurrentThreadHandle();
while (true) {
u32 value = LoadExclusive(std::addressof(this->thread_handle));
if (AMS_UNLIKELY(value != svc::InvalidHandle)) {
break;
}
DataMemoryBarrierForCriticalSection();
if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), cur_handle) == 0)) {
return true;
}
}
ClearExclusive();
DataMemoryBarrierForCriticalSection();
return false;
}
void InternalCriticalSectionImpl::Leave() {
AMS_ASSERT(svc::GetThreadLocalRegion()->disable_count == 0);
const auto cur_handle = GetCurrentThreadHandle();
u32 value = LoadExclusive(std::addressof(this->thread_handle));
while (true) {
if (AMS_UNLIKELY(value != cur_handle)) {
ClearExclusive();
break;
}
DataMemoryBarrierForCriticalSection();
if (AMS_LIKELY(StoreExclusive(std::addressof(this->thread_handle), 0) == 0)) {
break;
}
value = LoadExclusive(std::addressof(this->thread_handle));
}
DataMemoryBarrierForCriticalSection();
AMS_ASSERT((value | ams::svc::HandleWaitMask) == (cur_handle | ams::svc::HandleWaitMask));
if (value & ams::svc::HandleWaitMask) {
R_ABORT_UNLESS(ams::svc::ArbitrateUnlock(reinterpret_cast<uintptr_t>(std::addressof(this->thread_handle))));
}
}
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
const auto cur_handle = GetCurrentThreadHandle();
return (this->thread_handle & ~ams::svc::HandleWaitMask) == cur_handle;
}
}

View file

@ -0,0 +1,58 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_interrupt_event_target_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::os::InterruptEventImpl"
#endif
namespace ams::os::impl {
class InterruptEventImpl {
private:
InterruptEventTargetImpl impl;
public:
explicit InterruptEventImpl(InterruptName name, EventClearMode clear_mode) : impl(name, clear_mode) { /* ... */ }
void Clear() {
return this->impl.Clear();
}
void Wait() {
return this->impl.Wait();
}
bool TryWait() {
return this->impl.TryWait();
}
bool TimedWait(TimeSpan timeout) {
return this->impl.TimedWait(timeout);
}
TriBool IsSignaled() {
return this->impl.IsSignaled();
}
Handle GetHandle() const {
return this->impl.GetHandle();
}
};
}

View file

@ -0,0 +1,114 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_interrupt_event_target_impl.os.horizon.hpp"
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
InterruptEventHorizonImpl::InterruptEventHorizonImpl(InterruptName name, EventClearMode clear_mode) {
this->manual_clear = (clear_mode == EventClearMode_ManualClear);
auto interrupt_type = this->manual_clear ? svc::InterruptType_Level : svc::InterruptType_Edge;
svc::Handle handle;
R_ABORT_UNLESS(svc::CreateInterruptEvent(std::addressof(handle), static_cast<s32>(name), interrupt_type));
this->handle = handle;
}
InterruptEventHorizonImpl::~InterruptEventHorizonImpl() {
R_ABORT_UNLESS(svc::CloseHandle(this->handle));
}
void InterruptEventHorizonImpl::Clear() {
R_ABORT_UNLESS(svc::ClearEvent(this->handle));
}
void InterruptEventHorizonImpl::Wait() {
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, svc::WaitInfinite);
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (!this->manual_clear) {
R_TRY_CATCH(svc::ResetSignal(this->handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterruptEventHorizonImpl::TryWait() {
/* If we're auto clear, just try to reset. */
if (!this->manual_clear) {
return R_SUCCEEDED(svc::ResetSignal(this->handle));
}
/* Not auto-clear. */
while (true) {
/* Continuously wait, until success or timeout. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, 0);
/* If we succeeded, we're signaled. */
if (R_SUCCEEDED(res)) {
return true;
}
/* If we timed out, we're not signaled. */
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
bool InterruptEventHorizonImpl::TimedWait(TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
while (true) {
/* Continuously wait, until success. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(this->handle), 1, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds());
if (R_SUCCEEDED(res)) {
/* Clear, if we must. */
if (!this->manual_clear) {
R_TRY_CATCH(svc::ResetSignal(this->handle)) {
/* Some other thread might have caught this before we did. */
R_CATCH(svc::ResultInvalidState) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
}
return true;
}
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ASSERT(svc::ResultCancelled::Includes(res));
}
}
}

View file

@ -0,0 +1,45 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
class InterruptEventHorizonImpl {
private:
svc::Handle handle;
bool manual_clear;
public:
explicit InterruptEventHorizonImpl(InterruptName name, EventClearMode mode);
~InterruptEventHorizonImpl();
void Clear();
void Wait();
bool TryWait();
bool TimedWait(TimeSpan timeout);
TriBool IsSignaled() {
return TriBool::Undefined;
}
Handle GetHandle() const {
return this->handle;
}
};
using InterruptEventTargetImpl = InterruptEventHorizonImpl;
}

View file

@ -0,0 +1,24 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
void PushAndCheckLockLevel(MutexType *mutex);
void PopAndCheckLockLevel(MutexType *mutex);
}

View file

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "os_rng_manager_impl.hpp" #include "os_rng_manager_impl.hpp"
#include "os_thread_manager_types.hpp"
#include "os_tick_manager_impl.hpp" #include "os_tick_manager_impl.hpp"
namespace ams::os::impl { namespace ams::os::impl {
@ -24,12 +25,15 @@ namespace ams::os::impl {
private: private:
RngManager rng_manager{}; RngManager rng_manager{};
/* TODO */ /* TODO */
ThreadManager thread_manager{};
/* TODO */
TickManager tick_manager{}; TickManager tick_manager{};
/* TODO */ /* TODO */
public: public:
constexpr OsResourceManager() = default; OsResourceManager() = default;
constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; } constexpr ALWAYS_INLINE RngManager &GetRngManager() { return this->rng_manager; }
constexpr ALWAYS_INLINE ThreadManager &GetThreadManager() { return this->thread_manager; }
constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; } constexpr ALWAYS_INLINE TickManager &GetTickManager() { return this->tick_manager; }
}; };

View file

@ -15,6 +15,7 @@
*/ */
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "os_resource_manager.hpp"
namespace ams::os::impl { namespace ams::os::impl {

View file

@ -26,7 +26,7 @@ namespace ams::os::impl {
private: private:
void Initialize(); void Initialize();
public: public:
constexpr RngManager() : mt(), lock(), initialized() { /* ... */ } constexpr RngManager() : mt(), lock(false), initialized() { /* ... */ }
public: public:
u64 GenerateRandomU64(); u64 GenerateRandomU64();
}; };

View file

@ -0,0 +1,226 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_thread_manager.hpp"
#include "os_waitable_manager_impl.hpp"
#include "os_waitable_holder_base.hpp"
#include "os_waitable_holder_impl.hpp"
#include "os_waitable_object_list.hpp"
namespace ams::os::impl {
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority) {
/* Setup objects. */
new (GetPointer(thread->cs_thread)) impl::InternalCriticalSection;
new (GetPointer(thread->cv_thread)) impl::InternalConditionVariable;
new (GetPointer(thread->all_threads_node)) util::IntrusiveListNode;
new (GetPointer(thread->waitlist)) WaitableObjectList;
/* Set member variables. */
thread->thread_impl = (thread_impl != nullptr) ? thread_impl : std::addressof(thread->thread_impl_storage);
thread->function = function;
thread->argument = arg;
thread->stack = stack;
thread->stack_size = stack_size;
thread->base_priority = priority;
thread->suspend_count = 0;
thread->name_buffer[0] = '\x00';
thread->name_pointer = thread->name_buffer;
/* Mark initialized. */
thread->state = ThreadType::State_Initialized;
}
void ThreadManager::InvokeThread(ThreadType *thread) {
auto &manager = GetThreadManager();
manager.SetCurrentThread(thread);
manager.NotifyThreadNameChanged(thread);
{
std::unique_lock lk(GetReference(thread->cs_thread));
while (thread->state == ThreadType::State_Initialized) {
GetReference(thread->cv_thread).Wait(GetPointer(thread->cs_thread));
}
if (thread->state == ThreadType::State_Started) {
lk.unlock();
thread->function(thread->argument);
}
}
manager.CleanupThread();
}
ThreadManager::ThreadManager() : impl(std::addressof(main_thread)), total_thread_stack_size(0), num_created_threads(0) {
this->main_thread.state = ThreadType::State_Started;
this->SetCurrentThread(std::addressof(this->main_thread));
this->PlaceThreadObjectUnderThreadManagerSafe(std::addressof(this->main_thread));
}
void ThreadManager::CleanupThread() {
ThreadType *thread = this->GetCurrentThread();
{
std::scoped_lock lk(GetReference(thread->cs_thread));
thread->state = ThreadType::State_Terminated;
GetReference(thread->cv_thread).Broadcast();
GetReference(thread->waitlist).SignalAllThreads();
}
}
Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core) {
SetupThreadObjectUnsafe(thread, nullptr, function, argument, stack, stack_size, priority);
auto guard = SCOPE_GUARD { thread->state = ThreadType::State_NotInitialized; };
R_TRY(this->impl.CreateThread(thread, ideal_core));
guard.Cancel();
this->PlaceThreadObjectUnderThreadManagerSafe(thread);
return ResultSuccess();
}
Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority) {
return this->CreateThread(thread, function, argument, stack, stack_size, priority, this->impl.GetDefaultCoreNumber());
}
void ThreadManager::DestroyThread(ThreadType *thread) {
{
std::scoped_lock lk(GetReference(thread->cs_thread));
if (thread->state == ThreadType::State_Initialized) {
thread->state = ThreadType::State_DestroyedBeforeStarted;
this->impl.StartThread(thread);
GetReference(thread->cv_thread).Signal();
}
}
this->impl.WaitForThreadExit(thread);
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
{
std::scoped_lock lk(GetReference(thread->cs_thread));
/* NOTE: Here Nintendo would cleanup the alias stack. */
this->impl.DestroyThreadUnsafe(thread);
thread->state = ThreadType::State_NotInitialized;
GetReference(thread->waitlist).~WaitableObjectList();
thread->name_buffer[0] = '\x00';
{
std::scoped_lock tlk(this->cs);
this->EraseFromAllThreadsListUnsafe(thread);
}
}
}
void ThreadManager::StartThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
this->impl.StartThread(thread);
thread->state = ThreadType::State_Started;
GetReference(thread->cv_thread).Signal();
}
void ThreadManager::WaitThread(ThreadType *thread) {
this->impl.WaitForThreadExit(thread);
{
std::scoped_lock lk(GetReference(thread->cs_thread));
/* Note: Here Nintendo would cleanup the alias stack. */
}
}
bool ThreadManager::TryWaitThread(ThreadType *thread) {
const bool result = this->impl.TryWaitForThreadExit(thread);
if (result) {
std::scoped_lock lk(GetReference(thread->cs_thread));
/* Note: Here Nintendo would cleanup the alias stack. */
}
return result;
}
s32 ThreadManager::SuspendThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
auto prev_suspend_count = thread->suspend_count;
AMS_ASSERT(prev_suspend_count < ThreadSuspendCountMax);
thread->suspend_count = prev_suspend_count + 1;
if (prev_suspend_count == 0) {
this->impl.SuspendThreadUnsafe(thread);
}
return prev_suspend_count;
}
s32 ThreadManager::ResumeThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
auto prev_suspend_count = thread->suspend_count;
if (prev_suspend_count > 0) {
thread->suspend_count = prev_suspend_count - 1;
if (prev_suspend_count == 1) {
this->impl.ResumeThreadUnsafe(thread);
}
}
return prev_suspend_count;
}
void ThreadManager::CancelThreadSynchronization(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
this->impl.CancelThreadSynchronizationUnsafe(thread);
}
/* TODO void ThreadManager::GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
void ThreadManager::SetInitialThreadNameUnsafe(ThreadType *thread) {
if (thread == std::addressof(this->main_thread)) {
constexpr const char MainThreadName[] = "MainThread";
static_assert(sizeof(thread->name_buffer) >= sizeof(MainThreadName));
static_assert(MainThreadName[sizeof(MainThreadName) - 1] == '\x00');
std::memcpy(thread->name_buffer, MainThreadName, sizeof(MainThreadName));
} else {
constexpr const char ThreadNamePrefix[] = "Thread_0x";
constexpr size_t ThreadNamePrefixSize = sizeof(ThreadNamePrefix) - 1;
const u64 func = reinterpret_cast<u64>(thread->function);
static_assert(ThreadNamePrefixSize + sizeof(func) * 2 + 1 <= sizeof(thread->name_buffer));
std::snprintf(thread->name_buffer, sizeof(thread->name_buffer), "%s%016lX", ThreadNamePrefix, func);
}
thread->name_pointer = thread->name_buffer;
}
}

View file

@ -0,0 +1,40 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_thread_manager_types.hpp"
#include "os_resource_manager.hpp"
namespace ams::os::impl {
constexpr inline s32 CoreAffinityMaskBitWidth = BITSIZEOF(u64);
ALWAYS_INLINE ThreadManager &GetThreadManager() {
return GetResourceManager().GetThreadManager();
}
ALWAYS_INLINE ThreadType *GetCurrentThread() {
return GetThreadManager().GetCurrentThread();
}
ALWAYS_INLINE Handle GetCurrentThreadHandle() {
/* return GetCurrentThread()->thread_impl->handle; */
return ::threadGetCurHandle();
}
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority);
}

View file

@ -0,0 +1,214 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_thread_manager_impl.os.horizon.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
thread_local ThreadType *g_current_thread_pointer;
namespace {
s32 ConvertToHorizonPriority(s32 user_priority) {
const s32 horizon_priority = user_priority + UserThreadPriorityOffset;
AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority);
return horizon_priority;
}
s32 ConvertToUserPriority(s32 horizon_priority) {
AMS_ASSERT(HighestTargetThreadPriority <= horizon_priority && horizon_priority <= LowestTargetThreadPriority);
return horizon_priority - UserThreadPriorityOffset;
}
void InvokeThread(uintptr_t _thread) {
ThreadType *thread = reinterpret_cast<ThreadType *>(_thread);
/* Set the thread's id. */
u64 thread_id;
R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread->thread_impl->handle)));
thread->thread_id = thread_id;
/* Invoke the thread. */
ThreadManager::InvokeThread(thread);
}
}
ThreadManagerHorizonImpl::ThreadManagerHorizonImpl(ThreadType *main_thread) {
/* Get the thread impl object from libnx. */
ThreadImpl *thread_impl = ::threadGetSelf();
/* Get the thread priority. */
s32 horizon_priority;
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(horizon_priority), thread_impl->handle));
SetupThreadObjectUnsafe(main_thread, thread_impl, nullptr, nullptr, thread_impl->stack_mirror, thread_impl->stack_sz, ConvertToUserPriority(horizon_priority));
/* Set the thread id. */
u64 thread_id;
R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), svc::Handle(thread_impl->handle)));
main_thread->thread_id = thread_id;
/* NOTE: Here Nintendo would set the thread pointer in TLS. */
}
Result ThreadManagerHorizonImpl::CreateThread(ThreadType *thread, s32 ideal_core) {
/* Note: Here Nintendo would set the stack args to point to ThreadManager::InvokeThread. */
s32 count = 0;
while (true) {
R_TRY_CATCH(::threadCreate(thread->thread_impl, reinterpret_cast<::ThreadFunc>(&InvokeThread), thread, thread->stack, thread->stack_size, ConvertToHorizonPriority(thread->base_priority), ideal_core)) {
R_CATCH(svc::ResultOutOfResource) {
if ((++count) < 10) {
os::SleepThread(TimeSpan::FromMilliSeconds(10));
continue;
}
return os::ResultOutOfResource();
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ResultSuccess();
}
}
void ThreadManagerHorizonImpl::DestroyThreadUnsafe(ThreadType *thread) {
R_ABORT_UNLESS(::threadClose(thread->thread_impl));
}
void ThreadManagerHorizonImpl::StartThread(const ThreadType *thread) {
R_ABORT_UNLESS(::threadStart(thread->thread_impl));
}
void ThreadManagerHorizonImpl::WaitForThreadExit(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
while (true) {
s32 index;
R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, svc::WaitInfinite)) {
R_CATCH(svc::ResultCancelled) { continue; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return;
}
}
bool ThreadManagerHorizonImpl::TryWaitForThreadExit(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
while (true) {
/* Continuously wait, until success or timeout. */
s32 index;
Result res = svc::WaitSynchronization(std::addressof(index), std::addressof(handle), 1, 0);
/* If we succeeded, we're signaled. */
if (R_SUCCEEDED(res)) {
return true;
}
/* If we timed out, we're not signaled. */
if (svc::ResultTimedOut::Includes(res)) {
return false;
}
AMS_ABORT_UNLESS(svc::ResultCancelled::Includes(res));
}
}
void ThreadManagerHorizonImpl::YieldThread() {
if (hos::GetVersion() >= hos::Version_400) {
svc::SleepThread(svc::YieldType_WithCoreMigration);
} else {
svc::SleepThread(svc::YieldType_WithoutCoreMigration);
}
}
bool ThreadManagerHorizonImpl::ChangePriority(ThreadType *thread, s32 priority) {
const svc::Handle handle(thread->thread_impl->handle);
auto res = svc::SetThreadPriority(handle, ConvertToHorizonPriority(priority));
if (svc::ResultInvalidPriority::Includes(res)) {
AMS_ABORT("Invalid thread priority");
}
return R_SUCCEEDED(res);
}
s32 ThreadManagerHorizonImpl::GetCurrentPriority(const ThreadType *thread) const {
const svc::Handle handle(thread->thread_impl->handle);
s32 priority;
R_ABORT_UNLESS(svc::GetThreadPriority(std::addressof(priority), handle));
return ConvertToUserPriority(priority);
}
ThreadId ThreadManagerHorizonImpl::GetThreadId(const ThreadType *thread) const {
return thread->thread_id;
}
void ThreadManagerHorizonImpl::SuspendThreadUnsafe(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Paused));
}
void ThreadManagerHorizonImpl::ResumeThreadUnsafe(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::SetThreadActivity(handle, svc::ThreadActivity_Runnable));
}
void ThreadManagerHorizonImpl::CancelThreadSynchronizationUnsafe(ThreadType *thread) {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::CancelSynchronization(handle));
}
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
s32 ThreadManagerHorizonImpl::GetCurrentCoreNumber() const {
return svc::GetCurrentProcessorNumber();
}
void ThreadManagerHorizonImpl::SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const {
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::SetThreadCoreMask(handle, ideal_core, affinity_mask));
}
void ThreadManagerHorizonImpl::GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const {
s32 ideal_core;
u64 affinity_mask;
const svc::Handle handle(thread->thread_impl->handle);
R_ABORT_UNLESS(svc::GetThreadCoreMask(std::addressof(ideal_core), std::addressof(affinity_mask), handle));
if (out_ideal_core) {
*out_ideal_core = ideal_core;
}
if (out_affinity_mask) {
*out_affinity_mask = affinity_mask;
}
}
u64 ThreadManagerHorizonImpl::GetThreadAvailableCoreMask() const {
u64 core_mask;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(core_mask), svc::InfoType_CoreMask, svc::PseudoHandle::CurrentProcess, 0));
return core_mask;
}
}

View file

@ -0,0 +1,79 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::os::impl {
constexpr inline s32 TargetThreadPriorityRangeSize = svc::LowestThreadPriority - svc::HighestThreadPriority + 1;
constexpr inline s32 ReservedThreadPriorityRangeSize = svc::SystemThreadPriorityHighest;
constexpr inline s32 SystemThreadPriorityRangeSize = TargetThreadPriorityRangeSize - ReservedThreadPriorityRangeSize;
constexpr inline s32 UserThreadPriorityOffset = 28;
constexpr inline s32 HighestTargetThreadPriority = 0;
constexpr inline s32 LowestTargetThreadPriority = TargetThreadPriorityRangeSize - 1;
extern thread_local ThreadType *g_current_thread_pointer;
class ThreadManagerHorizonImpl {
NON_COPYABLE(ThreadManagerHorizonImpl);
NON_MOVEABLE(ThreadManagerHorizonImpl);
public:
explicit ThreadManagerHorizonImpl(ThreadType *main_thread);
Result CreateThread(ThreadType *thread, s32 ideal_core);
void DestroyThreadUnsafe(ThreadType *thread);
void StartThread(const ThreadType *thread);
void WaitForThreadExit(ThreadType *thread);
bool TryWaitForThreadExit(ThreadType *thread);
void YieldThread();
bool ChangePriority(ThreadType *thread, s32 priority);
s32 GetCurrentPriority(const ThreadType *thread) const;
ThreadId GetThreadId(const ThreadType *thread) const;
void SuspendThreadUnsafe(ThreadType *thread);
void ResumeThreadUnsafe(ThreadType *thread);
void CancelThreadSynchronizationUnsafe(ThreadType *thread);
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
void NotifyThreadNameChangedImpl(const ThreadType *thread) const { /* ... */ }
void SetCurrentThread(ThreadType *thread) const {
g_current_thread_pointer = thread;
}
ThreadType *GetCurrentThread() const {
return g_current_thread_pointer;
}
s32 GetCurrentCoreNumber() const;
s32 GetDefaultCoreNumber() const { return svc::IdealCoreUseProcessValue; }
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const;
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const;
u64 GetThreadAvailableCoreMask() const;
NORETURN void ExitProcessImpl() {
svc::ExitProcess();
AMS_ABORT("Process was exited");
}
};
using ThreadManagerImpl = ThreadManagerHorizonImpl;
}

View file

@ -0,0 +1,151 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#ifdef ATMOSPHERE_OS_HORIZON
#include "os_thread_manager_impl.os.horizon.hpp"
#else
#error "Unknown OS for ThreadManagerImpl"
#endif
namespace ams::os::impl {
class ThreadManager {
NON_COPYABLE(ThreadManager);
NON_MOVEABLE(ThreadManager);
private:
class ThreadListTraits {
public:
using ListType = util::IntrusiveList<ThreadType, ThreadListTraits>;
private:
friend class util::IntrusiveList<ThreadType, ThreadListTraits>;
static constexpr util::IntrusiveListNode &GetNode(ThreadType &parent) {
return GetReference(parent.all_threads_node);
}
static constexpr util::IntrusiveListNode const &GetNode(ThreadType const &parent) {
return GetReference(parent.all_threads_node);
}
static ThreadType &GetParent(util::IntrusiveListNode &node) {
return *reinterpret_cast<ThreadType *>(reinterpret_cast<char *>(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node));
}
static ThreadType const &GetParent(util::IntrusiveListNode const &node) {
return *reinterpret_cast<const ThreadType *>(reinterpret_cast<const char *>(std::addressof(node)) - OFFSETOF(ThreadType, all_threads_node));
}
};
using AllThreadsList = ThreadListTraits::ListType;
private:
ThreadManagerImpl impl;
ThreadType main_thread;
InternalCriticalSection cs;
AllThreadsList all_threads_list;
size_t total_thread_stack_size;
s32 num_created_threads;
public:
ThreadManager();
void CleanupThread();
s32 GetThreadCountForDebug() const { return this->num_created_threads; }
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority, s32 ideal_core);
Result CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority);
void DestroyThread(ThreadType *thread);
void StartThread(ThreadType *thread);
void WaitThread(ThreadType *thread);
bool TryWaitThread(ThreadType *thread);
void YieldThread() { return this->impl.YieldThread(); }
bool ChangePriority(ThreadType *thread, s32 priority) { return this->impl.ChangePriority(thread, priority); }
s32 GetCurrentPriority(const ThreadType *thread) const { return this->impl.GetCurrentPriority(thread); }
ThreadType *GetCurrentThread() const { return this->impl.GetCurrentThread(); }
s32 SuspendThread(ThreadType *thread);
s32 ResumeThread(ThreadType *thread);
void CancelThreadSynchronization(ThreadType *thread);
/* TODO void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
void SetInitialThreadNameUnsafe(ThreadType *thread);
void NotifyThreadNameChanged(const ThreadType *thread) const { return this->impl.NotifyThreadNameChangedImpl(thread); }
void SetCurrentThread(ThreadType *thread) const { return this->impl.SetCurrentThread(thread); }
s32 GetCurrentCoreNumber() const { return this->impl.GetCurrentCoreNumber(); }
void SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const { return this->impl.SetThreadCoreMask(thread, ideal_core, affinity_mask); }
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const { return this->impl.GetThreadCoreMask(out_ideal_core, out_affinity_mask, thread); }
u64 GetThreadAvailableCoreMask() const { return this->impl.GetThreadAvailableCoreMask(); }
void PushBackToAllThreadsListUnsafe(ThreadType *thread) {
this->all_threads_list.push_back(*thread);
++this->num_created_threads;
this->total_thread_stack_size += thread->stack_size;
}
void EraseFromAllThreadsListUnsafe(ThreadType *thread) {
this->all_threads_list.erase(this->all_threads_list.iterator_to(*thread));
--this->num_created_threads;
this->total_thread_stack_size -= thread->stack_size;
}
void PushBackToAllThreadsListSafe(ThreadType *thread) {
std::scoped_lock lk(this->cs);
this->PushBackToAllThreadsListUnsafe(thread);
}
void EraseFromAllThreadsListSafe(ThreadType *thread) {
std::scoped_lock lk(this->cs);
this->EraseFromAllThreadsListUnsafe(thread);
}
void PlaceThreadObjectUnderThreadManagerSafe(ThreadType *thread) {
SetInitialThreadNameUnsafe(thread);
{
std::scoped_lock lk(this->cs);
this->PushBackToAllThreadsListUnsafe(thread);
}
}
ThreadType *AllocateThreadType() const {
return reinterpret_cast<ThreadType *>(std::malloc(sizeof(ThreadType)));
}
void FreeThreadType(ThreadType *thread) const {
std::free(thread);
}
const ThreadType *GetMainThread() const {
return std::addressof(this->main_thread);
}
size_t GetTotalThreadStackSize() const {
return this->total_thread_stack_size;
}
ThreadId GetThreadId(const ThreadType *thread) {
return this->impl.GetThreadId(thread);
}
public:
static void InvokeThread(ThreadType *thread);
};
}

View file

@ -0,0 +1,37 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
TargetTimeSpan TimeoutHelper::GetTimeLeftOnTarget() const {
/* If the absolute tick is zero, we're expired. */
if (this->absolute_end_tick.GetInt64Value() == 0) {
return 0;
}
/* Check if we've expired. */
const Tick cur_tick = impl::GetTickManager().GetTick();
if (cur_tick >= this->absolute_end_tick) {
return 0;
}
/* Return the converted difference as a timespan. */
return TimeoutHelperImpl::ConvertToImplTime(this->absolute_end_tick - cur_tick);
}
}

View file

@ -0,0 +1,64 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_tick_manager.hpp"
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_timeout_helper_impl.os.horizon.hpp"
#else
#error "Unknown OS for ams::os::TimeoutHelper"
#endif
namespace ams::os::impl {
class TimeoutHelper {
private:
Tick absolute_end_tick;
public:
explicit TimeoutHelper(TimeSpan timeout) {
if (timeout == 0) {
/* If timeout is zero, don't do relative tick calculations. */
this->absolute_end_tick = Tick(0);
} else {
const auto &tick_manager = impl::GetTickManager();
const u64 cur_tick = tick_manager.GetTick().GetInt64Value();
const u64 timeout_tick = tick_manager.ConvertToTick(timeout).GetInt64Value();
const u64 end_tick = cur_tick + timeout_tick + 1;
this->absolute_end_tick = Tick(std::min<u64>(std::numeric_limits<s64>::max(), end_tick));
}
}
static void Sleep(TimeSpan tm) {
TimeoutHelperImpl::Sleep(tm);
}
bool TimedOut() const {
if (this->absolute_end_tick.GetInt64Value() == 0) {
return true;
}
const Tick cur_tick = impl::GetTickManager().GetTick();
return cur_tick >= this->absolute_end_tick;
}
TargetTimeSpan GetTimeLeftOnTarget() const;
};
}

View file

@ -0,0 +1,30 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "os_timeout_helper_impl.os.horizon.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
void TimeoutHelperImpl::Sleep(TimeSpan tm) {
if (tm == TimeSpan(0)) {
GetThreadManager().YieldThread();
} else {
ams::svc::SleepThread(tm.GetNanoSeconds());
}
}
}

View file

@ -0,0 +1,33 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_tick_manager.hpp"
namespace ams::os::impl {
using TargetTimeSpan = ::ams::TimeSpan;
class TimeoutHelperImpl {
public:
static TargetTimeSpan ConvertToImplTime(Tick tick) {
return impl::GetTickManager().ConvertToTimeSpan(tick);
}
static void Sleep(TimeSpan tm);
};
}

View file

@ -37,8 +37,8 @@ namespace ams::os::impl {
/* Gets handle to output, returns INVALID_HANDLE on failure. */ /* Gets handle to output, returns INVALID_HANDLE on failure. */
virtual Handle GetHandle() const = 0; virtual Handle GetHandle() const = 0;
/* Gets the amount of time remaining until this wakes up. */ /* Gets the amount of time remaining until this wakes up. */
virtual u64 GetWakeupTime() const { virtual TimeSpan GetAbsoluteWakeupTime() const {
return std::numeric_limits<u64>::max(); return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
} }
/* Interface with manager. */ /* Interface with manager. */
@ -58,18 +58,18 @@ namespace ams::os::impl {
class WaitableHolderOfUserObject : public WaitableHolderBase { class WaitableHolderOfUserObject : public WaitableHolderBase {
public: public:
/* All user objects have no handle to wait on. */ /* All user objects have no handle to wait on. */
virtual Handle GetHandle() const override { virtual Handle GetHandle() const override final {
return INVALID_HANDLE; return svc::InvalidHandle;
} }
}; };
class WaitableHolderOfKernelObject : public WaitableHolderBase { class WaitableHolderOfKernelObject : public WaitableHolderBase {
public: public:
/* All kernel objects have native handles, and thus don't have object list semantics. */ /* All kernel objects have native handles, and thus don't have object list semantics. */
virtual TriBool LinkToObjectList() override { virtual TriBool LinkToObjectList() override final {
return TriBool::Undefined; return TriBool::Undefined;
} }
virtual void UnlinkFromObjectList() override { virtual void UnlinkFromObjectList() override final {
/* ... */ /* ... */
} }
}; };

View file

@ -51,6 +51,6 @@ namespace ams::os::impl {
#undef CHECK_HOLDER #undef CHECK_HOLDER
static_assert(std::is_trivial<WaitableHolderImpl>::value && std::is_trivially_destructible<WaitableHolderImpl>::value, "WaitableHolderImpl"); static_assert(std::is_trivial<WaitableHolderImpl>::value && std::is_trivially_destructible<WaitableHolderImpl>::value);
static_assert(sizeof(WaitableHolderImpl) == WaitableHolder::ImplStorageSize, "WaitableHolderImpl size"); static_assert(sizeof(WaitableHolderImpl) == sizeof(os::WaitableHolderType::impl_storage));
} }

View file

@ -21,29 +21,29 @@ namespace ams::os::impl {
class WaitableHolderOfEvent : public WaitableHolderOfUserObject { class WaitableHolderOfEvent : public WaitableHolderOfUserObject {
private: private:
Event *event; EventType *event;
private: private:
TriBool IsSignaledImpl() const { TriBool IsSignaledImpl() const {
return this->event->signaled ? TriBool::True : TriBool::False; return this->event->signaled ? TriBool::True : TriBool::False;
} }
public: public:
explicit WaitableHolderOfEvent(Event *e) : event(e) { /* ... */ } explicit WaitableHolderOfEvent(EventType *e) : event(e) { /* ... */ }
/* IsSignaled, Link, Unlink implemented. */ /* IsSignaled, Link, Unlink implemented. */
virtual TriBool IsSignaled() const override { virtual TriBool IsSignaled() const override {
std::scoped_lock lk(this->event->lock); std::scoped_lock lk(GetReference(this->event->cs_event));
return this->IsSignaledImpl(); return this->IsSignaledImpl();
} }
virtual TriBool LinkToObjectList() override { virtual TriBool LinkToObjectList() override {
std::scoped_lock lk(this->event->lock); std::scoped_lock lk(GetReference(this->event->cs_event));
GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this); GetReference(this->event->waitable_object_list_storage).LinkWaitableHolder(*this);
return this->IsSignaledImpl(); return this->IsSignaledImpl();
} }
virtual void UnlinkFromObjectList() override { virtual void UnlinkFromObjectList() override {
std::scoped_lock lk(this->event->lock); std::scoped_lock lk(GetReference(this->event->cs_event));
GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this); GetReference(this->event->waitable_object_list_storage).UnlinkWaitableHolder(*this);
} }

Some files were not shown because too many files have changed in this diff Show more