mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
os: refactor/rewrite entire namespace.
This commit is contained in:
parent
6193283f03
commit
065485b971
181 changed files with 5353 additions and 1929 deletions
|
@ -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));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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 *);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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>
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(¬ify_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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 = {};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
/* ... */
|
/* ... */
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
24
libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp
Normal file
24
libraries/libstratosphere/source/os/impl/os_mutex_impl.hpp
Normal 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);
|
||||||
|
|
||||||
|
}
|
|
@ -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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
226
libraries/libstratosphere/source/os/impl/os_thread_manager.cpp
Normal file
226
libraries/libstratosphere/source/os/impl/os_thread_manager.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
||||||
/* ... */
|
/* ... */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
Loading…
Reference in a new issue