ams: support building unit test programs on windows/linux/macos

This commit is contained in:
Michael Scire
2022-03-06 12:08:20 -08:00
committed by SciresM
parent 9a38be201a
commit 64a97576d0
756 changed files with 33359 additions and 9372 deletions

View File

@@ -47,8 +47,8 @@ namespace ams::os::impl {
}
#ifdef ATMOSPHERE_OS_HORIZON
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_address_space_allocator_impl.os.horizon.hpp"
#else
#error "Unknown OS for AddressSpaceAllocator"
#include "os_address_space_allocator_impl.generic.hpp"
#endif

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 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 AddressSpaceAllocator final : public AddressSpaceAllocatorBase<uintptr_t, size_t> {
private:
using Base = AddressSpaceAllocatorBase<uintptr_t, size_t>;
public:
using Base::Base;
public:
virtual bool CheckFreeSpace(uintptr_t address, size_t size) override {
AMS_UNUSED(address, size);
return true;
}
};
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 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_address_space_allocator_forbidden_region.hpp"
namespace ams::os::impl {
class AslrSpaceManagerLinuxImpl {
NON_COPYABLE(AslrSpaceManagerLinuxImpl);
NON_MOVEABLE(AslrSpaceManagerLinuxImpl);
public:
constexpr AslrSpaceManagerLinuxImpl() = default;
static constexpr ALWAYS_INLINE const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() {
return nullptr;
}
static constexpr ALWAYS_INLINE size_t GetForbiddenRegionCount() {
return 0;
}
static constexpr ALWAYS_INLINE u64 GetHeapSpaceBeginAddress() {
return 8_GB;
}
static constexpr ALWAYS_INLINE u64 GetHeapSpaceSize() {
return 4_GB;
}
static constexpr ALWAYS_INLINE u64 GetAliasSpaceBeginAddress() {
return 60_GB;
}
static constexpr ALWAYS_INLINE u64 GetAliasSpaceSize() {
return 4_GB;
}
static constexpr ALWAYS_INLINE u64 GetAslrSpaceBeginAddress() {
return 2_MB;
}
static constexpr ALWAYS_INLINE u64 GetAslrSpaceEndAddress() {
return 64_GB;
}
};
using AslrSpaceManagerImpl = AslrSpaceManagerLinuxImpl;
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 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_address_space_allocator_forbidden_region.hpp"
namespace ams::os::impl {
class AslrSpaceManagerLinuxImpl {
NON_COPYABLE(AslrSpaceManagerLinuxImpl);
NON_MOVEABLE(AslrSpaceManagerLinuxImpl);
public:
constexpr AslrSpaceManagerLinuxImpl() = default;
static constexpr ALWAYS_INLINE const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() {
return nullptr;
}
static constexpr ALWAYS_INLINE size_t GetForbiddenRegionCount() {
return 0;
}
static constexpr ALWAYS_INLINE u64 GetHeapSpaceBeginAddress() {
return 8_GB;
}
static constexpr ALWAYS_INLINE u64 GetHeapSpaceSize() {
return 4_GB;
}
static constexpr ALWAYS_INLINE u64 GetAliasSpaceBeginAddress() {
return 60_GB;
}
static constexpr ALWAYS_INLINE u64 GetAliasSpaceSize() {
return 4_GB;
}
static constexpr ALWAYS_INLINE u64 GetAslrSpaceBeginAddress() {
return 2_MB;
}
static constexpr ALWAYS_INLINE u64 GetAslrSpaceEndAddress() {
return 64_GB;
}
};
using AslrSpaceManagerImpl = AslrSpaceManagerLinuxImpl;
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 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_address_space_allocator_forbidden_region.hpp"
namespace ams::os::impl {
class AslrSpaceManagerWindowsImpl {
NON_COPYABLE(AslrSpaceManagerWindowsImpl);
NON_MOVEABLE(AslrSpaceManagerWindowsImpl);
public:
constexpr AslrSpaceManagerWindowsImpl() = default;
static constexpr ALWAYS_INLINE const AddressSpaceAllocatorForbiddenRegion *GetForbiddenRegions() {
return nullptr;
}
static constexpr ALWAYS_INLINE size_t GetForbiddenRegionCount() {
return 0;
}
static constexpr ALWAYS_INLINE u64 GetHeapSpaceBeginAddress() {
return 8_GB;
}
static constexpr ALWAYS_INLINE u64 GetHeapSpaceSize() {
return 4_GB;
}
static constexpr ALWAYS_INLINE u64 GetAliasSpaceBeginAddress() {
return 60_GB;
}
static constexpr ALWAYS_INLINE u64 GetAliasSpaceSize() {
return 4_GB;
}
static constexpr ALWAYS_INLINE u64 GetAslrSpaceBeginAddress() {
return 2_MB;
}
static constexpr ALWAYS_INLINE u64 GetAslrSpaceEndAddress() {
return 64_GB;
}
};
using AslrSpaceManagerImpl = AslrSpaceManagerWindowsImpl;
}

View File

@@ -17,8 +17,14 @@
#include <stratosphere.hpp>
#include "os_address_space_allocator.hpp"
#ifdef ATMOSPHERE_OS_HORIZON
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_aslr_space_manager_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_aslr_space_manager_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_aslr_space_manager_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_aslr_space_manager_impl.os.macos.hpp"
#else
#error "Unknown OS for AslrSpaceManagerImpl"
#endif

View File

@@ -16,8 +16,14 @@
#pragma once
#include <stratosphere.hpp>
#ifdef ATMOSPHERE_OS_HORIZON
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_cache_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_cache_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_cache_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_cache_impl.os.linux.hpp"
#else
#error "Unknown OS for ThreadManagerImpl"
#error "Unknown OS for CacheImpl"
#endif

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 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 {
inline void FlushDataCacheImpl(const void *addr, size_t size) {
#if defined(ATMOSPHERE_ARCH_ARM64)
{
/* Declare helper variables. */
uintptr_t cache_type_register = 0;
uintptr_t cache_line_size = 0;
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
/* Get the cache type register. */
__asm__ __volatile__("mrs %[cache_type_register], ctr_el0" : [cache_type_register]"=r"(cache_type_register));
/* Calculate cache line size. */
cache_line_size = 4 << ((cache_type_register >> 16) & 0xF);
/* Iterate, flushing cache lines. */
for (uintptr_t cur = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1); cur < end_addr; cur += cache_line_size) {
__asm__ __volatile__ ("dc civac, %[cur]" :: [cur]"r"(cur));
}
/* Insert a memory barrier, now that memory has been flushed. */
__asm__ __volatile__("dsb sy" ::: "memory");
}
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
/* Theoretically, do nothing here? */
AMS_UNUSED(addr, size);
__asm__ __volatile__("" ::: "memory");
#else
#error "Unknown architecture for linux DataCache"
#endif
}
inline void FlushEntireDataCacheImpl() {
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
/* Theoretically, do nothing here? */
__asm__ __volatile__("" ::: "memory");
#elif defined(ATMOSPHERE_ARCH_ARM64)
AMS_ABORT("FlushEntireDataCacheImpl called on arm64 linux");
#else
#error "Unknown architecture for linux DataCache"
#endif
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 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 {
inline void FlushDataCacheImpl(const void *addr, size_t size) {
#if defined(ATMOSPHERE_ARCH_ARM64)
{
/* Declare helper variables. */
uintptr_t cache_type_register = 0;
uintptr_t cache_line_size = 0;
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
/* Get the cache type register. */
__asm__ __volatile__("mrs %[cache_type_register], ctr_el0" : [cache_type_register]"=r"(cache_type_register));
/* Calculate cache line size. */
cache_line_size = 4 << ((cache_type_register >> 16) & 0xF);
/* Iterate, flushing cache lines. */
for (uintptr_t cur = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1); cur < end_addr; cur += cache_line_size) {
__asm__ __volatile__ ("dc civac, %[cur]" :: [cur]"r"(cur));
}
/* Insert a memory barrier, now that memory has been flushed. */
__asm__ __volatile__("dsb sy" ::: "memory");
}
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
/* Theoretically, do nothing here? */
AMS_UNUSED(addr, size);
__asm__ __volatile__("" ::: "memory");
#else
#error "Unknown architecture for macOS DataCache"
#endif
}
inline void FlushEntireDataCacheImpl() {
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
/* Theoretically, do nothing here? */
__asm__ __volatile__("" ::: "memory");
#elif defined(ATMOSPHERE_ARCH_ARM64)
AMS_ABORT("FlushEntireDataCacheImpl called on arm64 macOS");
#else
#error "Unknown architecture for macOS DataCache"
#endif
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 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 {
inline void FlushDataCacheImpl(const void *addr, size_t size) {
#if defined(ATMOSPHERE_ARCH_ARM64)
{
/* Declare helper variables. */
uintptr_t cache_type_register = 0;
uintptr_t cache_line_size = 0;
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
/* Get the cache type register. */
__asm__ __volatile__("mrs %[cache_type_register], ctr_el0" : [cache_type_register]"=r"(cache_type_register));
/* Calculate cache line size. */
cache_line_size = 4 << ((cache_type_register >> 16) & 0xF);
/* Iterate, flushing cache lines. */
for (uintptr_t cur = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1); cur < end_addr; cur += cache_line_size) {
__asm__ __volatile__ ("dc civac, %[cur]" :: [cur]"r"(cur));
}
/* Insert a memory barrier, now that memory has been flushed. */
__asm__ __volatile__("dsb sy" ::: "memory");
}
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
/* Theoretically, do nothing here? */
AMS_UNUSED(addr, size);
__asm__ __volatile__("" ::: "memory");
#else
#error "Unknown architecture for windows DataCache"
#endif
}
inline void FlushEntireDataCacheImpl() {
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
/* Theoretically, do nothing here? */
__asm__ __volatile__("" ::: "memory");
#else
#error "Unknown architecture for windows DataCache"
#endif
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 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_giant_lock_types.hpp"
#include "os_resource_manager.hpp"
namespace ams::os::impl {
ALWAYS_INLINE GiantLock &GetGiantLock() {
return GetResourceManager().GetGiantLock();
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 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 GiantLockHorizonImpl{};
using GiantLock = GiantLockHorizonImpl;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 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_giant_lock.os.linux.hpp"
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
namespace ams::os::impl {
#define AMS_OS_IMPL_GIANT_LOCK_FILE_PATH "/tmp/.ams_os_giant_lock"
void GiantLockLinuxImpl::Lock() {
m_fd = ::open(AMS_OS_IMPL_GIANT_LOCK_FILE_PATH, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
AMS_ABORT_UNLESS(m_fd >= 0);
while (::lockf(m_fd, F_LOCK, 0) < 0) {
AMS_ABORT_UNLESS(errno == EINTR);
}
}
void GiantLockLinuxImpl::Unlock() {
AMS_ASSERT(m_fd >= 0);
while (::lockf(m_fd, F_ULOCK, 0) < 0) {
AMS_ABORT_UNLESS(errno == EINTR);
}
while (::close(m_fd) < 0) {
AMS_ABORT_UNLESS(errno == EINTR);
}
m_fd = os::InvalidNativeHandle;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 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 GiantLockLinuxImpl {
private:
s32 m_fd = -1;
public:
void Lock();
void Unlock();
ALWAYS_INLINE void lock() {
this->Lock();
}
ALWAYS_INLINE void unlock() {
this->Unlock();
}
};
using GiantLock = GiantLockLinuxImpl;
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 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_giant_lock.os.macos.hpp"
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
namespace ams::os::impl {
#define AMS_OS_IMPL_GIANT_LOCK_FILE_PATH "/tmp/.ams_os_giant_lock"
void GiantLockMacosImpl::Lock() {
m_fd = ::open(AMS_OS_IMPL_GIANT_LOCK_FILE_PATH, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
AMS_ABORT_UNLESS(m_fd >= 0);
while (::lockf(m_fd, F_LOCK, 0) < 0) {
AMS_ABORT_UNLESS(errno == EINTR);
}
}
void GiantLockMacosImpl::Unlock() {
AMS_ASSERT(m_fd >= 0);
while (::lockf(m_fd, F_ULOCK, 0) < 0) {
AMS_ABORT_UNLESS(errno == EINTR);
}
while (::close(m_fd) < 0) {
AMS_ABORT_UNLESS(errno == EINTR);
}
m_fd = os::InvalidNativeHandle;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 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 GiantLockMacosImpl {
private:
s32 m_fd = -1;
public:
void Lock();
void Unlock();
ALWAYS_INLINE void lock() {
this->Lock();
}
ALWAYS_INLINE void unlock() {
this->Unlock();
}
};
using GiantLock = GiantLockMacosImpl;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 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_giant_lock.os.windows.hpp"
namespace ams::os::impl {
GiantLockWindowsImpl::GiantLockWindowsImpl() {
const NativeHandle handle = ::CreateMutexA(nullptr, false, "AMS_OS_STRATOSPHERE_GIANT_LOCK");
AMS_ASSERT(handle != InvalidNativeHandle);
m_handle = handle;
}
GiantLockWindowsImpl::~GiantLockWindowsImpl() {
const auto ret = ::CloseHandle(m_handle);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 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 GiantLockWindowsImpl {
private:
NativeHandle m_handle;
public:
GiantLockWindowsImpl();
~GiantLockWindowsImpl();
ALWAYS_INLINE void lock() {
::WaitForSingleObject(m_handle, INFINITE);
}
ALWAYS_INLINE void unlock() {
::ReleaseMutex(m_handle);
}
};
using GiantLock = GiantLockWindowsImpl;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 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_giant_lock.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_giant_lock.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_giant_lock.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_giant_lock.os.macos.hpp"
#else
#error "Unknown OS for GiantLock"
#endif

View File

@@ -0,0 +1,178 @@
/*
* Copyright (c) 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_resource_manager.hpp"
extern "C" { extern u8 __argdata__[]; }
namespace ams::os {
namespace {
class MemoryArranger {
private:
uintptr_t m_address;
public:
constexpr MemoryArranger(uintptr_t address) : m_address(address) { /* ... */ }
template<typename T>
T *Arrange() {
this->Align(alignof(T));
return static_cast<T *>(this->Arrange(sizeof(T)));
}
void *Arrange(size_t size) {
const auto address = m_address;
m_address += size;
return reinterpret_cast<void *>(address);
}
void Align(size_t align) {
m_address = util::AlignUp(m_address, align);
}
char *ArrangeCharArray(size_t size) {
return reinterpret_cast<char *>(Arrange(size));
}
};
bool HasArguments(uintptr_t args_region) {
/* Check that the arguments region is read-write. */
svc::MemoryInfo mi;
svc::PageInfo pi;
if (R_FAILED(svc::QueryMemory(std::addressof(mi), std::addressof(pi), args_region))) {
return false;
}
return mi.permission == svc::MemoryPermission_ReadWrite;
}
const char *SkipSpace(const char *p, const char *end) {
while (p < end && std::isspace(*p)) { ++p; }
return p;
}
const char *GetTokenEnd(const char *p, const char *end) {
while (p < end && !std::isspace(*p)) { ++p; }
return p;
}
const char *GetQuotedTokenEnd(const char *p, const char *end) {
while (p < end && *p != '"') { ++p; }
return p;
}
int MakeArgv(char **out_argv_buf, char *arg_buf, const char *cmd_line, size_t cmd_line_size, int arg_max) {
/* Prepare to parse arguments. */
auto idx = 0;
auto src = cmd_line;
auto dst = arg_buf;
const auto end = src + cmd_line_size;
/* Parse all tokens. */
while (true) {
/* Advance past any spaces. */
src = SkipSpace(src, end);
if (src >= end) {
break;
}
/* Check that we don't have too many arguments. */
if (idx >= arg_max) {
break;
}
/* Find the start/end of the current argument token. */
const char *arg_end;
const char *src_next;
if (*src == '"') {
++src;
arg_end = GetQuotedTokenEnd(src, end);
src_next = arg_end + 1;
} else {
arg_end = GetTokenEnd(src, end);
src_next = arg_end;
}
/* Determine token size. */
const auto arg_size = arg_end - src;
/* Set the argv pointer. */
out_argv_buf[idx++] = dst;
/* Copy the argument. */
std::memcpy(dst, src, arg_size);
dst += arg_size;
/* Null-terminate the argument token. */
*(dst++) = '\x00';
/* Advance to next token. */
src = src_next;
}
/* Null terminate the final token. */
*(dst++) = '\x00';
/* Null terminate argv. */
out_argv_buf[idx] = nullptr;
return idx;
}
}
void SetHostArgc(int argc);
void SetHostArgv(char **argv);
void Initialize() {
/* Only allow os::Initialize to be called once. */
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
if (s_initialized) {
return;
}
s_initialized = true;
/* Initialize the global os resource manager. */
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
/* Setup host argc/argv as needed. */
const uintptr_t args_region = reinterpret_cast<uintptr_t>(__argdata__);
if (HasArguments(args_region)) {
/* Create arguments memory arranger. */
MemoryArranger arranger(args_region);
/* Arrange. */
const auto &header = *arranger.Arrange<ldr::ProgramArguments>();
const char *cmd_line = arranger.ArrangeCharArray(header.arguments_size);
char *arg_buf = arranger.ArrangeCharArray(header.arguments_size + 2);
char **argv_buf = arranger.Arrange<char *>();
/* Determine extents. */
const auto arg_buf_size = reinterpret_cast<uintptr_t>(argv_buf) - args_region;
const auto arg_max = (header.allocated_size - arg_buf_size) / sizeof(char *);
/* Make argv. */
const auto arg_count = MakeArgv(argv_buf, arg_buf, cmd_line, header.arguments_size, arg_max);
/* Set host argc/argv. */
os::SetHostArgc(arg_count);
os::SetHostArgv(argv_buf);
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 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_resource_manager.hpp"
namespace ams {
void Main();
namespace init {
void InitializeDefaultAllocator();
}
/* TODO: This should probably instead be a custom init::Initialize*()? */
namespace fs {
void InitializeForHostTool();
}
}
namespace ams::os {
void SetHostArgc(int argc);
void SetHostArgv(char **argv);
[[gnu::constructor(101)]] void Initialize() {
/* Only allow os::Initialize to be called once. */
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
if (s_initialized) {
return;
}
s_initialized = true;
/* Initialize the global os resource manager. */
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
/* Initialize virtual address memory. */
os::InitializeVirtualAddressMemory();
/* Ensure that the init library's allocator has been setup. */
init::InitializeDefaultAllocator();
}
}
extern "C" int main(int argc, char **argv) {
/* Ensure os library is initialized. */
::ams::os::Initialize();
/* Set argc/argv. */
::ams::os::SetHostArgc(argc);
::ams::os::SetHostArgv(argv);
/* Ensure filesystem library is initialized. */
::ams::fs::InitializeForHostTool();
/* Call main. */
::ams::Main();
/* TODO: Should we try to implement a custom exit here? */
return 0;
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 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_resource_manager.hpp"
namespace ams {
void Main();
namespace init {
void InitializeDefaultAllocator();
}
/* TODO: This should probably instead be a custom init::Initialize*()? */
namespace fs {
void InitializeForHostTool();
}
}
namespace ams::os {
void SetHostArgc(int argc);
void SetHostArgv(char **argv);
[[gnu::constructor(101)]] void Initialize() {
/* Only allow os::Initialize to be called once. */
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
if (s_initialized) {
return;
}
s_initialized = true;
/* Initialize the global os resource manager. */
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
/* Initialize virtual address memory. */
os::InitializeVirtualAddressMemory();
/* Ensure that the init library's allocator has been setup. */
init::InitializeDefaultAllocator();
}
}
extern "C" int main(int argc, char **argv) {
/* Ensure os library is initialized. */
::ams::os::Initialize();
/* Set argc/argv. */
::ams::os::SetHostArgc(argc);
::ams::os::SetHostArgv(argv);
/* Ensure filesystem library is initialized. */
::ams::fs::InitializeForHostTool();
/* Call main. */
::ams::Main();
/* TODO: Should we try to implement a custom exit here? */
return 0;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 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_resource_manager.hpp"
namespace ams {
void Main();
namespace init {
void InitializeDefaultAllocator();
}
/* TODO: This should probably instead be a custom init::Initialize*()? */
namespace fs {
void InitializeForHostTool();
}
}
namespace ams::os {
void SetHostArgc(int argc);
void SetHostArgv(char **argv);
namespace {
void SetupWindowsConsole(DWORD which) {
/* Get handle to standard device. */
const auto handle = ::GetStdHandle(which);
if (handle == INVALID_HANDLE_VALUE) {
return;
}
/* Get the console mode. */
DWORD mode;
if (!::GetConsoleMode(handle, std::addressof(mode))) {
return;
}
/* Enable printing with ANSI escape codes. */
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
/* Set console mode. */
::SetConsoleMode(handle, mode);
}
}
void Initialize() {
/* Only allow os::Initialize to be called once. */
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(bool, s_initialized, false);
if (s_initialized) {
return;
}
s_initialized = true;
/* Initialize the global os resource manager. */
os::impl::ResourceManagerHolder::InitializeResourceManagerInstance();
/* Initialize virtual address memory. */
os::InitializeVirtualAddressMemory();
/* Ensure that the init library's allocator has been setup. */
init::InitializeDefaultAllocator();
/* Try to set up the windows console. */
SetupWindowsConsole(STD_OUTPUT_HANDLE);
SetupWindowsConsole(STD_ERROR_HANDLE);
}
}
extern "C" int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
/* Ensure os library is initialized. */
::ams::os::Initialize();
/* Set argc/argv. */
::ams::os::SetHostArgc(__argc);
::ams::os::SetHostArgv(__argv);
/* Ensure filesystem library is initialized. */
::ams::fs::InitializeForHostTool();
/* Call main. */
::ams::Main();
/* TODO: Should we try to implement a custom exit here? */
return 0;
}

View File

@@ -18,6 +18,12 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_inter_process_event_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_inter_process_event_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_inter_process_event_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_inter_process_event_impl.os.macos.hpp"
#else
#error "Unknown OS for ams::os::InterProcessEventImpl"
#endif

View File

@@ -20,7 +20,7 @@
namespace ams::os::impl {
Result InterProcessEventImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
Result InterProcessEventHorizonImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
/* Create the event handles. */
svc::Handle wh, rh;
R_TRY_CATCH(svc::CreateEvent(std::addressof(wh), std::addressof(rh))) {
@@ -32,21 +32,21 @@ namespace ams::os::impl {
return ResultSuccess();
}
void InterProcessEventImpl::Close(NativeHandle handle) {
void InterProcessEventHorizonImpl::Close(NativeHandle handle) {
if (handle != os::InvalidNativeHandle) {
R_ABORT_UNLESS(svc::CloseHandle(handle));
}
}
void InterProcessEventImpl::Signal(NativeHandle handle) {
void InterProcessEventHorizonImpl::Signal(NativeHandle handle) {
R_ABORT_UNLESS(svc::SignalEvent(handle));
}
void InterProcessEventImpl::Clear(NativeHandle handle) {
void InterProcessEventHorizonImpl::Clear(NativeHandle handle) {
R_ABORT_UNLESS(svc::ClearEvent(handle));
}
void InterProcessEventImpl::Wait(NativeHandle handle, bool auto_clear) {
void InterProcessEventHorizonImpl::Wait(NativeHandle handle, bool auto_clear) {
while (true) {
/* Continuously wait, until success. */
s32 index;
@@ -67,7 +67,7 @@ namespace ams::os::impl {
}
}
bool InterProcessEventImpl::TryWait(NativeHandle handle, bool auto_clear) {
bool InterProcessEventHorizonImpl::TryWait(NativeHandle handle, bool auto_clear) {
/* If we're auto clear, just try to reset. */
if (auto_clear) {
return R_SUCCEEDED(svc::ResetSignal(handle));
@@ -93,7 +93,7 @@ namespace ams::os::impl {
}
}
bool InterProcessEventImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
bool InterProcessEventHorizonImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
while (true) {

View File

@@ -18,7 +18,7 @@
namespace ams::os::impl {
class InterProcessEventImpl {
class InterProcessEventHorizonImpl {
public:
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
static void Close(NativeHandle handle);
@@ -29,4 +29,6 @@ namespace ams::os::impl {
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
};
using InterProcessEventImpl = InterProcessEventHorizonImpl;
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright (c) 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.linux.hpp"
#include "os_timeout_helper.hpp"
#include <unistd.h>
#include <sys/eventfd.h>
#include <poll.h>
namespace ams::os::impl {
namespace {
bool PollEvent(NativeHandle handle, s64 ns) {
struct pollfd pfd;
pfd.fd = handle;
pfd.events = POLLIN;
pfd.revents = 0;
/* Determine timeout. */
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
struct timespec ts = { .tv_sec = (ns / NanoSecondsPerSecond), .tv_nsec = ns % NanoSecondsPerSecond };
s32 res;
do {
res = ::ppoll(std::addressof(pfd), 1, ns >= 0 ? std::addressof(ts) : nullptr, nullptr);
} while (res < 0 && errno == EINTR);
AMS_ASSERT(res == 0);
return pfd.revents & POLLIN;
}
}
bool InterProcessEventLinuxImpl::ResetEventSignal(NativeHandle handle) {
u64 dummy;
s32 res;
do {
res = ::read(handle, std::addressof(dummy), sizeof(dummy));
} while (res < 0 && errno == EINTR);
AMS_ASSERT(res == sizeof(u64) || (res < 0 && errno == EAGAIN));
if (res == sizeof(u64)) {
AMS_ASSERT(dummy > 0);
}
return res == sizeof(u64);
}
Result InterProcessEventLinuxImpl::CreateSingle(NativeHandle *out_event) {
/* Create the event handle. */
os::NativeHandle event;
do {
event = ::eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
} while (event < 0 && errno == EINTR);
R_UNLESS(event != os::InvalidNativeHandle, os::ResultOutOfResource());
/* Set the output. */
*out_event = event;
R_SUCCEED();
}
Result InterProcessEventLinuxImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
/* Create the writable handle. */
os::NativeHandle write;
R_TRY(CreateSingle(std::addressof(write)));
ON_RESULT_FAILURE { Close(write); };
/* Create the read handle. */
os::NativeHandle read;
do {
read = ::dup(write);
} while (read < 0 && errno == EINTR);
R_UNLESS(read != os::InvalidNativeHandle, os::ResultOutOfResource());
/* Set the output. */
*out_write = write;
*out_read = read;
R_SUCCEED();
}
void InterProcessEventLinuxImpl::Close(NativeHandle handle) {
if (handle != os::InvalidNativeHandle) {
s32 ret;
do {
ret = ::close(handle);
} while (ret < 0 && errno == EINTR);
AMS_ASSERT(ret == 0);
}
}
void InterProcessEventLinuxImpl::Signal(NativeHandle handle) {
const u64 counter_add = 1;
s32 ret;
do {
ret = ::write(handle, std::addressof(counter_add), sizeof(counter_add));
} while (ret < 0 && errno == EINTR);
AMS_ASSERT(ret == sizeof(counter_add));
}
void InterProcessEventLinuxImpl::Clear(NativeHandle handle) {
ResetEventSignal(handle);
}
void InterProcessEventLinuxImpl::Wait(NativeHandle handle, bool auto_clear) {
while (true) {
/* Continuously wait, until success. */
auto ret = PollEvent(handle, -1);
AMS_ASSERT(ret);
AMS_UNUSED(ret);
/* If we're not obligated to clear, we're done. */
if (!auto_clear) {
return;
}
/* Try to reset. */
if (ResetEventSignal(handle)) {
return;
}
}
}
bool InterProcessEventLinuxImpl::TryWait(NativeHandle handle, bool auto_clear) {
/* If we're auto clear, just try to reset. */
if (auto_clear) {
return ResetEventSignal(handle);
}
return PollEvent(handle, 0);
}
bool InterProcessEventLinuxImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
do {
if (const auto res = PollEvent(handle, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds()); res) {
/* If we're not obligated to clear, we're done. */
if (!auto_clear) {
return true;
}
/* Try to reset. */
if (ResetEventSignal(handle)) {
return true;
}
}
} while (!timeout_helper.TimedOut());
return false;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 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 InterProcessEventLinuxImpl {
public:
static Result CreateSingle(NativeHandle *out_event);
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
static void Close(NativeHandle handle);
static void Signal(NativeHandle handle);
static void Clear(NativeHandle handle);
static void Wait(NativeHandle handle, bool auto_clear);
static bool TryWait(NativeHandle handle, bool auto_clear);
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
private:
static bool ResetEventSignal(NativeHandle handle);
};
using InterProcessEventImpl = InterProcessEventLinuxImpl;
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 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.macos.hpp"
#include "os_timeout_helper.hpp"
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
namespace ams::os::impl {
namespace {
/* On macOS, the maximum size of a pipe buffer is 64_KB. */
static constexpr size_t PipeBufferSizeMax = 64_KB;
constinit char g_shared_pipe_read_buffer[PipeBufferSizeMax];
bool PollEvent(NativeHandle handle, s64 ns) {
struct pollfd pfd;
pfd.fd = handle;
pfd.events = POLLIN;
pfd.revents = 0;
/* Determine timeout. */
constexpr s64 NanoSecondsPerMilliSecond = TimeSpan::FromMilliSeconds(1).GetNanoSeconds();
/* TODO: Will macos ever support ppoll? */
const int timeout = static_cast<int>(ns >= 0 ? (ns / NanoSecondsPerMilliSecond) : -1);
s32 res;
do {
res = ::poll(std::addressof(pfd), 1, timeout);
} while (res < 0 && errno == EINTR);
AMS_ASSERT(res == 0);
return pfd.revents & POLLIN;
}
}
bool InterProcessEventMacosImpl::ResetEventSignal(NativeHandle handle) {
s32 res;
do {
res = ::read(handle, g_shared_pipe_read_buffer, sizeof(g_shared_pipe_read_buffer));
} while (res < 0 && errno == EINTR);
if (res > 0) {
AMS_ASSERT(res <= static_cast<s32>(sizeof(g_shared_pipe_read_buffer)));
} else {
AMS_ASSERT(res < 0);
AMS_ASSERT(errno == EAGAIN);
}
return res > 0;
}
Result InterProcessEventMacosImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
/* Create the handles. */
os::NativeHandle handles[2];
s32 res;
do {
res = ::pipe(handles);
} while (res < 0 && errno == EINTR);
R_UNLESS(res == 0, os::ResultOutOfResource());
ON_RESULT_FAILURE { Close(handles[0]); Close(handles[1]); };
/* Set as non-blocking. */
do {
res = ::fcntl(handles[0], F_SETFL, O_NONBLOCK);
} while (res < 0 && errno == EINTR);
R_UNLESS(res == 0, os::ResultOutOfResource());
do {
res = ::fcntl(handles[1], F_SETFL, O_NONBLOCK);
} while (res < 0 && errno == EINTR);
R_UNLESS(res == 0, os::ResultOutOfResource());
/* Set the output. */
*out_read = handles[0];
*out_write = handles[1];
R_SUCCEED();
}
void InterProcessEventMacosImpl::Close(NativeHandle handle) {
if (handle != os::InvalidNativeHandle) {
s32 ret;
do {
ret = ::close(handle);
} while (ret < 0 && errno == EINTR);
AMS_ASSERT(ret == 0);
}
}
void InterProcessEventMacosImpl::Signal(NativeHandle handle) {
const u8 data = 0xCC;
s32 ret;
do {
ret = ::write(handle, std::addressof(data), sizeof(data));
} while (ret < 0 && errno == EINTR);
AMS_ASSERT(ret == sizeof(data) || (ret < 0 && errno == EAGAIN));
}
void InterProcessEventMacosImpl::Clear(NativeHandle handle) {
ResetEventSignal(handle);
}
void InterProcessEventMacosImpl::Wait(NativeHandle handle, bool auto_clear) {
while (true) {
/* Continuously wait, until success. */
auto ret = PollEvent(handle, -1);
AMS_ASSERT(ret);
AMS_UNUSED(ret);
/* If we're not obligated to clear, we're done. */
if (!auto_clear) {
return;
}
/* Try to reset. */
if (ResetEventSignal(handle)) {
return;
}
}
}
bool InterProcessEventMacosImpl::TryWait(NativeHandle handle, bool auto_clear) {
/* If we're auto clear, just try to reset. */
if (auto_clear) {
return ResetEventSignal(handle);
}
return PollEvent(handle, 0);
}
bool InterProcessEventMacosImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
do {
if (const auto res = PollEvent(handle, timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds()); res) {
/* If we're not obligated to clear, we're done. */
if (!auto_clear) {
return true;
}
/* Try to reset. */
if (ResetEventSignal(handle)) {
return true;
}
}
} while (!timeout_helper.TimedOut());
return false;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 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 InterProcessEventMacosImpl {
public:
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
static void Close(NativeHandle handle);
static void Signal(NativeHandle handle);
static void Clear(NativeHandle handle);
static void Wait(NativeHandle handle, bool auto_clear);
static bool TryWait(NativeHandle handle, bool auto_clear);
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
private:
static bool ResetEventSignal(NativeHandle handle);
};
using InterProcessEventImpl = InterProcessEventMacosImpl;
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 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.windows.hpp"
#include "os_giant_lock.hpp"
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
bool InterProcessEventWindowsImpl::ResetEventSignal(NativeHandle handle) {
std::scoped_lock lk(GetGiantLock());
if (auto ret = ::WaitForSingleObject(handle, 0); ret == WAIT_OBJECT_0) {
::ResetEvent(handle);
return true;
} else {
return false;
}
}
Result InterProcessEventWindowsImpl::Create(NativeHandle *out_write, NativeHandle *out_read) {
/* Create the writable handle. */
auto write = ::CreateEvent(nullptr, true, false, nullptr);
AMS_ASSERT(write != os::InvalidNativeHandle);
/* Create the read handle. */
os::NativeHandle read;
const auto cur_proc = ::GetCurrentProcess();
const auto ret = ::DuplicateHandle(cur_proc, write, cur_proc, std::addressof(read), 0, false, DUPLICATE_SAME_ACCESS);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
/* Set the output. */
*out_write = write;
*out_read = read;
R_SUCCEED();
}
void InterProcessEventWindowsImpl::Close(NativeHandle handle) {
if (handle != os::InvalidNativeHandle) {
const auto ret = ::CloseHandle(handle);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
}
void InterProcessEventWindowsImpl::Signal(NativeHandle handle) {
const auto ret = ::SetEvent(handle);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
void InterProcessEventWindowsImpl::Clear(NativeHandle handle) {
const auto ret = ::ResetEvent(handle);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
void InterProcessEventWindowsImpl::Wait(NativeHandle handle, bool auto_clear) {
while (true) {
/* Continuously wait, until success. */
auto ret = ::WaitForSingleObject(handle, INFINITE);
AMS_ASSERT(ret == WAIT_OBJECT_0);
AMS_UNUSED(ret);
/* If we're not obligated to clear, we're done. */
if (!auto_clear) {
return;
}
/* Try to reset. */
if (ResetEventSignal(handle)) {
return;
}
}
}
bool InterProcessEventWindowsImpl::TryWait(NativeHandle handle, bool auto_clear) {
/* If we're auto clear, just try to reset. */
if (auto_clear) {
return ResetEventSignal(handle);
}
const auto ret = ::WaitForSingleObject(handle, 0);
AMS_ASSERT((ret == WAIT_OBJECT_0) || (ret == WAIT_TIMEOUT));
return ret == WAIT_OBJECT_0;
}
bool InterProcessEventWindowsImpl::TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
do {
if (const auto res = ::WaitForSingleObject(handle, timeout_helper.GetTimeLeftOnTarget()); res == WAIT_OBJECT_0) {
/* If we're not obligated to clear, we're done. */
if (!auto_clear) {
return true;
}
/* Try to reset. */
if (ResetEventSignal(handle)) {
return true;
}
}
} while (!timeout_helper.TimedOut());
return false;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 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 InterProcessEventWindowsImpl {
public:
static Result Create(NativeHandle *out_write, NativeHandle *out_read);
static void Close(NativeHandle handle);
static void Signal(NativeHandle handle);
static void Clear(NativeHandle handle);
static void Wait(NativeHandle handle, bool auto_clear);
static bool TryWait(NativeHandle handle, bool auto_clear);
static bool TimedWait(NativeHandle handle, bool auto_clear, TimeSpan timeout);
private:
static bool ResetEventSignal(NativeHandle handle);
};
using InterProcessEventImpl = InterProcessEventWindowsImpl;
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 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_internal_critical_section_impl.os.windows.hpp"
#include "os_timeout_helper.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
struct WindowsConditionVariable {
CONDITION_VARIABLE cv;
};
void InternalConditionVariableImpl::Initialize() {
::InitializeConditionVariable(std::addressof(util::GetReference(m_windows_cv_storage).cv));
}
void InternalConditionVariableImpl::Finalize() {
/* ... */
}
void InternalConditionVariableImpl::Signal() {
::WakeConditionVariable(std::addressof(util::GetReference(m_windows_cv_storage).cv));
}
void InternalConditionVariableImpl::Broadcast() {
::WakeAllConditionVariable(std::addressof(util::GetReference(m_windows_cv_storage).cv));
}
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
::SleepConditionVariableCS(std::addressof(util::GetReference(m_windows_cv_storage).cv), std::addressof(util::GetReference(cs->Get()->m_windows_critical_section_storage).cs), INFINITE);
}
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
const auto ret = ::SleepConditionVariableCS(std::addressof(util::GetReference(m_windows_cv_storage).cv), std::addressof(util::GetReference(cs->Get()->m_windows_critical_section_storage).cs), timeout_helper.GetTimeLeftOnTarget());
if (ret == 0) {
if (::GetLastError() == ERROR_TIMEOUT) {
return ConditionVariableStatus::TimedOut;
}
}
return ConditionVariableStatus::Success;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 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"
#if defined(AMS_OS_IMPL_USE_PTHREADS)
namespace ams::os::impl {
namespace {
}
void InternalConditionVariableImpl::Initialize() {
/* TODO: What should be done here? */
}
void InternalConditionVariableImpl::Finalize() {
/* TODO: What should be done here? */
}
void InternalConditionVariableImpl::Signal() {
AMS_ABORT_UNLESS(pthread_cond_signal(std::addressof(m_pthread_cond)) == 0);
}
void InternalConditionVariableImpl::Broadcast() {
AMS_ABORT_UNLESS(pthread_cond_broadcast(std::addressof(m_pthread_cond)) == 0);
}
void InternalConditionVariableImpl::Wait(InternalCriticalSection *cs) {
AMS_ABORT_UNLESS(pthread_cond_wait(std::addressof(m_pthread_cond), std::addressof(cs->Get()->m_pthread_mutex)) == 0);
}
ConditionVariableStatus InternalConditionVariableImpl::TimedWait(InternalCriticalSection *cs, const TimeoutHelper &timeout_helper) {
struct timespec ts;
const auto gettime_res = clock_gettime(CLOCK_REALTIME, std::addressof(ts));
AMS_ASSERT(gettime_res == 0);
AMS_UNUSED(gettime_res);
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
const s64 ns = timeout_helper.GetTimeLeftOnTarget().GetNanoSeconds();
ts.tv_sec += (ns / NanoSecondsPerSecond);
ts.tv_nsec += (ns % NanoSecondsPerSecond);
if (ts.tv_nsec >= NanoSecondsPerSecond) {
ts.tv_sec += ts.tv_nsec / NanoSecondsPerSecond;
ts.tv_nsec %= NanoSecondsPerSecond;
}
const auto res = pthread_cond_timedwait(std::addressof(m_pthread_cond), std::addressof(cs->Get()->m_pthread_mutex), std::addressof(ts));
if (res != 0) {
AMS_ABORT_UNLESS(errno == ETIMEDOUT);
return ConditionVariableStatus::TimedOut;
}
return ConditionVariableStatus::Success;
}
}
#endif

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 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_internal_critical_section_impl.os.windows.hpp"
namespace ams::os::impl {
void InternalCriticalSectionImpl::Initialize() {
::InitializeCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
}
void InternalCriticalSectionImpl::Finalize() {
::DeleteCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
}
void InternalCriticalSectionImpl::Enter() {
::EnterCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
}
bool InternalCriticalSectionImpl::TryEnter() {
return ::TryEnterCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs)) != 0;
}
void InternalCriticalSectionImpl::Leave() {
return ::LeaveCriticalSection(std::addressof(util::GetReference(m_windows_critical_section_storage).cs));
}
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
/* Get the cs. */
CRITICAL_SECTION * const cs = std::addressof(util::GetReference(m_windows_critical_section_storage).cs);
/* If the critical section has no owning thread, it's not locked. */
if (cs->OwningThread == nullptr) {
return false;
}
/* If it has an owner, TryLock() will succeed if and only if we own the critical section. */
if (::TryEnterCriticalSection(cs) == 0) {
return false;
}
/* We now hold the critical section. To avoid a race, check that we didn't just acquire it by chance. */
const auto holds_lock = cs->RecursionCount > 1;
/* Leave, since we just successfully entered. */
::LeaveCriticalSection(cs);
return holds_lock;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 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 {
struct WindowsCriticalSection {
mutable CRITICAL_SECTION cs;
};
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 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>
#if defined(ATMOSPHERE_OS_LINUX)
#include <sys/syscall.h>
#include <unistd.h>
#endif
#if defined(AMS_OS_IMPL_USE_PTHREADS)
namespace ams::os::impl {
#if defined(AMS_OS_INTERNAL_CRITICAL_SECTION_IMPL_CAN_CHECK_LOCKED_BY_CURRENT_THREAD)
namespace {
#if defined(ATMOSPHERE_OS_LINUX)
template<typename T>
concept IsGlibcPthreadMutexImplementationWithOwnerField = requires (T &t) {
{ t.__data.__owner } -> std::convertible_to<int>;
};
static_assert(IsGlibcPthreadMutexImplementationWithOwnerField<pthread_mutex_t>);
#endif
}
#endif
void InternalCriticalSectionImpl::Initialize() {
/* TODO: What should be done here? */
}
void InternalCriticalSectionImpl::Finalize() {
/* TODO: What should be done here? */
}
void InternalCriticalSectionImpl::Enter() {
AMS_ABORT_UNLESS(pthread_mutex_lock(std::addressof(m_pthread_mutex)) == 0);
}
bool InternalCriticalSectionImpl::TryEnter() {
return pthread_mutex_trylock(std::addressof(m_pthread_mutex)) == 0;
}
void InternalCriticalSectionImpl::Leave() {
AMS_ABORT_UNLESS(pthread_mutex_unlock(std::addressof(m_pthread_mutex)) == 0);
}
#if defined(AMS_OS_INTERNAL_CRITICAL_SECTION_IMPL_CAN_CHECK_LOCKED_BY_CURRENT_THREAD)
bool InternalCriticalSectionImpl::IsLockedByCurrentThread() const {
/* TODO: This should be less of a terrible hack. */
#if defined(ATMOSPHERE_OS_LINUX)
return m_pthread_mutex.__data.__owner == ::syscall(SYS_gettid);
#else
#error "Unknown OS/underlying implementation for pthread InternalCriticalSectionImpl::IsLockedByCurrentThread()"
#endif
}
#endif
}
#endif

View File

@@ -0,0 +1,241 @@
/*
* Copyright (c) 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 {
namespace {
constexpr inline u8 LightEventState_NotSignaled = -1;
constexpr inline u8 LightEventState_Invalid = 0;
constexpr inline u8 LightEventState_Signaled = 1;
static_assert(LightEventState_NotSignaled == 0xFF);
ALWAYS_INLINE bool AtomicAutoClearLightEvent(std::atomic<u8> &signal_state) {
u8 expected = LightEventState_Signaled;
return signal_state.compare_exchange_strong(expected, LightEventState_NotSignaled);
}
ALWAYS_INLINE u32 GetBroadcastCounterUnsafe(u16 &low, u8 &high) {
const u32 upper = high;
return (upper << BITSIZEOF(low)) | low;
}
ALWAYS_INLINE void IncrementBroadcastCounterUnsafe(u16 &low, u8 &high) {
if ((++low) == 0) {
++high;
}
}
}
void InternalLightEventImpl::Initialize(bool signaled) {
/* Set broadcast counter. */
m_counter_low = 0;
m_counter_high = 0;
/* Set initial state. */
m_signal_state = signaled ? LightEventState_Signaled : LightEventState_NotSignaled;
}
void InternalLightEventImpl::Finalize() {
/* Set final state. */
m_signal_state = LightEventState_Invalid;
}
void InternalLightEventImpl::SignalWithAutoClear() {
/* Signal only if we need to. */
if (m_signal_state.load(std::memory_order_acquire) == LightEventState_NotSignaled) {
/* Lock ourselves. */
std::scoped_lock lk(m_cs);
/* Set our state to signaled. */
m_signal_state = LightEventState_Signaled;
/* Signal to any waiters. */
m_cv.Signal();
}
}
void InternalLightEventImpl::SignalWithManualClear() {
/* Signal only if we need to. */
if (m_signal_state.load(std::memory_order_acquire) == LightEventState_NotSignaled) {
/* Lock ourselves. */
std::scoped_lock lk(m_cs);
/* Set our state to signaled. */
m_signal_state = LightEventState_Signaled;
/* Increment our broadcast counter. */
IncrementBroadcastCounterUnsafe(m_counter_low, m_counter_high);
/* Broadcast to any waiters. */
m_cv.Broadcast();
}
}
void InternalLightEventImpl::Clear() {
/* Clear only if we need to. */
if (m_signal_state.load(std::memory_order_acquire) == LightEventState_Signaled) {
/* Lock ourselves. */
std::scoped_lock lk(m_cs);
/* Set our state to signaled. */
m_signal_state = LightEventState_NotSignaled;
}
}
void InternalLightEventImpl::WaitWithAutoClear() {
/* Loop waiting. */
while (true) {
/* Get the current state. */
const auto state = m_signal_state.load(std::memory_order_acquire);
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
if (state == LightEventState_NotSignaled) {
break;
}
/* If we're signaled, try to reset. */
if (state == LightEventState_Signaled && AtomicAutoClearLightEvent(m_signal_state)) {
return;
}
}
/* Wait explicitly on the not-signaled event. */
std::scoped_lock lk(m_cs);
while (m_signal_state == LightEventState_NotSignaled) {
m_cv.Wait(std::addressof(m_cs));
}
/* Set our state to not signaled. */
m_signal_state = LightEventState_NotSignaled;
}
void InternalLightEventImpl::WaitWithManualClear() {
/* Loop waiting. */
while (true) {
/* Get the current m_signal_state. */
const auto state = m_signal_state.load(std::memory_order_acquire);
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
if (state == LightEventState_NotSignaled) {
break;
}
/* If we're signaled, we're done. */
if (state == LightEventState_Signaled) {
return;
}
}
/* Wait explicitly on the not-signaled event. */
std::scoped_lock lk(m_cs);
/* Get the broadcast counter. */
const auto bc = GetBroadcastCounterUnsafe(m_counter_low, m_counter_high);
while (m_signal_state == LightEventState_NotSignaled) {
/* Check if a broadcast has occurred. */
if (bc != GetBroadcastCounterUnsafe(m_counter_low, m_counter_high)) {
break;
}
m_cv.Wait(std::addressof(m_cs));
}
}
bool InternalLightEventImpl::TryWaitWithAutoClear() {
return m_signal_state.load(std::memory_order_acquire) == LightEventState_Signaled && AtomicAutoClearLightEvent(m_signal_state);
}
bool InternalLightEventImpl::TryWaitWithManualClear() {
return m_signal_state.load(std::memory_order_acquire) == LightEventState_Signaled;
}
bool InternalLightEventImpl::TimedWaitWithAutoClear(const TimeoutHelper &timeout_helper) {
/* Loop waiting. */
while (true) {
/* Get the current state. */
const auto state = m_signal_state.load(std::memory_order_acquire);
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
if (state == LightEventState_NotSignaled) {
break;
}
/* If we're signaled, try to reset. */
if (state == LightEventState_Signaled && AtomicAutoClearLightEvent(m_signal_state)) {
return true;
}
}
/* Wait explicitly on the not-signaled event. */
std::scoped_lock lk(m_cs);
while (m_signal_state == LightEventState_NotSignaled) {
if (m_cv.TimedWait(std::addressof(m_cs), timeout_helper) == ConditionVariableStatus::TimedOut) {
return false;
}
}
/* Set our state to not signaled. */
m_signal_state = LightEventState_NotSignaled;
return true;
}
bool InternalLightEventImpl::TimedWaitWithManualClear(const TimeoutHelper &timeout_helper) {
/* Loop waiting. */
while (true) {
/* Get the current m_signal_state. */
const auto state = m_signal_state.load(std::memory_order_acquire);
/* If we're not signaled, stop looping so we can properly wait to be signaled. */
if (state == LightEventState_NotSignaled) {
break;
}
/* If we're signaled, we're done. */
if (state == LightEventState_Signaled) {
return true;
}
}
/* Wait explicitly on the not-signaled event. */
std::scoped_lock lk(m_cs);
/* Get the broadcast counter. */
const auto bc = GetBroadcastCounterUnsafe(m_counter_low, m_counter_high);
while (m_signal_state == LightEventState_NotSignaled) {
/* Check if a broadcast has occurred. */
if (bc != GetBroadcastCounterUnsafe(m_counter_low, m_counter_high)) {
break;
}
if (m_cv.TimedWait(std::addressof(m_cs), timeout_helper) == ConditionVariableStatus::TimedOut) {
return false;
}
}
return true;
}
}

View File

@@ -20,28 +20,6 @@ namespace ams::os::impl {
namespace {
constexpr inline u8 WriterCountMax = std::numeric_limits<u8>::max();
ALWAYS_INLINE u16 GetReaderCount(u32 v) {
return static_cast<u16>(v >> 0);
}
ALWAYS_INLINE u8 GetWriterCurrent(u32 v) {
return static_cast<u8>(v >> 16);
}
ALWAYS_INLINE u8 GetWriterNext(u32 v) {
return static_cast<u8>(v >> 24);
}
ALWAYS_INLINE u32 IncrementWriterNext(u32 v) {
return v + (1u << 24);
}
ALWAYS_INLINE bool IsWriteLocked(u32 v) {
return GetWriterCurrent(v) != GetWriterNext(v);
}
ALWAYS_INLINE void PrefetchForBusyMutex(u32 *p) {
/* Nintendo does PRFM pstl1keep. */
__builtin_prefetch(p, 1);
@@ -79,16 +57,8 @@ namespace ams::os::impl {
return result == 0;
}
ALWAYS_INLINE u8 *GetWriterCurrentPointerForBusyMutex(u32 *p) {
if constexpr (util::IsLittleEndian()) {
return reinterpret_cast<u8 *>(reinterpret_cast<uintptr_t>(p)) + 2;
} else {
return reinterpret_cast<u8 *>(reinterpret_cast<uintptr_t>(p)) + 1;
}
}
ALWAYS_INLINE void StoreReleaseWriteLockValueForBusyMutex(u32 *p) {
u8 * const p8 = GetWriterCurrentPointerForBusyMutex(p);
u8 * const p8 = InternalReaderWriterBusyMutexValue::GetWriterCurrentPointer(p);
u8 v;
__asm__ __volatile__("ldrb %w[v], %[p8]\n"
@@ -125,11 +95,11 @@ namespace ams::os::impl {
const u32 v = LoadAcquireExclusiveForBusyMutex(p);
/* We can only acquire read lock if not write-locked. */
const bool write_locked = IsWriteLocked(v);
const bool write_locked = InternalReaderWriterBusyMutexValue::IsWriteLocked(v);
if (AMS_LIKELY(!write_locked)) {
/* Check that we don't overflow the reader count. */
const u32 new_v = v + 1;
AMS_ABORT_UNLESS(GetReaderCount(new_v) != 0);
AMS_ABORT_UNLESS(InternalReaderWriterBusyMutexValue::GetReaderCount(new_v) != 0);
/* Try to store our updated lock value. */
if (AMS_LIKELY(StoreExclusiveForBusyMutex(p, new_v))) {
@@ -162,7 +132,7 @@ namespace ams::os::impl {
do {
/* Get and validate the current value. */
v = LoadExclusiveForBusyMutex(p);
AMS_ABORT_UNLESS(GetReaderCount(v) != 0);
AMS_ABORT_UNLESS(InternalReaderWriterBusyMutexValue::GetReaderCount(v) != 0);
} while (!StoreReleaseExclusiveForBusyMutex(p, v - 1));
}
@@ -207,10 +177,10 @@ namespace ams::os::impl {
const u32 v = LoadAcquireExclusiveForBusyMutex(p);
/* Check that we can write lock. */
AMS_ABORT_UNLESS(static_cast<u8>(GetWriterNext(v) - GetWriterCurrent(v)) < WriterCountMax);
AMS_ABORT_UNLESS(static_cast<u8>(InternalReaderWriterBusyMutexValue::GetWriterNext(v) - InternalReaderWriterBusyMutexValue::GetWriterCurrent(v)) < InternalReaderWriterBusyMutexValue::WriterCountMax);
/* Determine our write-lock number. */
const u32 new_v = IncrementWriterNext(v);
const u32 new_v = InternalReaderWriterBusyMutexValue::IncrementWriterNext(v);
/* Try to store our updated lock value. */
if (AMS_UNLIKELY(!StoreExclusiveForBusyMutex(p, new_v))) {
@@ -226,7 +196,7 @@ namespace ams::os::impl {
}
/* Wait until the lock is truly acquired. */
if (GetReaderCount(new_v) != 0 || GetWriterNext(v) != GetWriterCurrent(new_v)) {
if (InternalReaderWriterBusyMutexValue::GetReaderCount(new_v) != 0 || InternalReaderWriterBusyMutexValue::GetWriterNext(v) != InternalReaderWriterBusyMutexValue::GetWriterCurrent(new_v)) {
/* Send an event, so that we can immediately wait without fail. */
SendEventLocalForBusyMutex();
@@ -236,7 +206,7 @@ namespace ams::os::impl {
/* Get the updated value. */
const u32 cur_v = LoadAcquireExclusiveForBusyMutex(p);
if (GetReaderCount(cur_v) == 0 && GetWriterNext(v) == GetWriterCurrent(cur_v)) {
if (InternalReaderWriterBusyMutexValue::GetReaderCount(cur_v) == 0 && InternalReaderWriterBusyMutexValue::GetWriterNext(v) == InternalReaderWriterBusyMutexValue::GetWriterCurrent(cur_v)) {
break;
}
}
@@ -249,7 +219,7 @@ namespace ams::os::impl {
void InternalReaderWriterBusyMutexImpl::ReleaseWriteLock() {
/* Check pre-conditions. */
AMS_ABORT_UNLESS(IsWriteLocked(m_value));
AMS_ABORT_UNLESS(InternalReaderWriterBusyMutexValue::IsWriteLocked(m_value));
/* Get pointer to our value. */
u32 * const p = std::addressof(m_value);

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 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 {
/* TODO */
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 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 {
/* TODO */
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 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 {
/* TODO */
}

View File

@@ -18,6 +18,12 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_interrupt_event_target_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_interrupt_event_target_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_interrupt_event_target_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_interrupt_event_target_impl.os.macos.hpp"
#else
#error "Unknown OS for ams::os::InterruptEventImpl"
#endif

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 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 InterruptEventLinuxImpl {
private:
NativeHandle m_handle;
bool m_manual_clear;
public:
explicit InterruptEventLinuxImpl(InterruptName name, EventClearMode mode) {
AMS_UNUSED(name, mode);
AMS_ABORT("TODO");
}
~InterruptEventLinuxImpl() {
AMS_ABORT("TODO");
}
void Clear() { AMS_ABORT("TODO"); }
void Wait() { AMS_ABORT("TODO"); }
bool TryWait() { AMS_ABORT("TODO"); }
bool TimedWait(TimeSpan) { AMS_ABORT("TODO"); }
TriBool IsSignaled() {
return TriBool::Undefined;
}
NativeHandle GetHandle() const {
return m_handle;
}
};
using InterruptEventTargetImpl = InterruptEventLinuxImpl;
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 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 InterruptEventMacosImpl {
private:
NativeHandle m_handle;
bool m_manual_clear;
public:
explicit InterruptEventMacosImpl(InterruptName name, EventClearMode mode) {
AMS_UNUSED(name, mode);
AMS_ABORT("TODO");
}
~InterruptEventMacosImpl() {
AMS_ABORT("TODO");
}
void Clear() { AMS_ABORT("TODO"); }
void Wait() { AMS_ABORT("TODO"); }
bool TryWait() { AMS_ABORT("TODO"); }
bool TimedWait(TimeSpan) { AMS_ABORT("TODO"); }
TriBool IsSignaled() {
return TriBool::Undefined;
}
NativeHandle GetHandle() const {
return m_handle;
}
};
using InterruptEventTargetImpl = InterruptEventMacosImpl;
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 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.windows.hpp"
#include "os_timeout_helper.hpp"
#include "os_giant_lock.hpp"
namespace ams::os::impl {
InterruptEventWindowsImpl::InterruptEventWindowsImpl(InterruptName name, EventClearMode clear_mode) {
const NativeHandle handle = ::OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, reinterpret_cast<LPCTSTR>(name));
AMS_ASSERT(handle != InvalidNativeHandle);
m_handle = handle;
m_manual_clear = (clear_mode == EventClearMode_ManualClear);
}
InterruptEventWindowsImpl::~InterruptEventWindowsImpl() {
const auto ret = ::CloseHandle(m_handle);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
bool InterruptEventWindowsImpl::ResetEventSignal() {
std::scoped_lock lk(GetGiantLock());
if (auto ret = ::WaitForSingleObject(m_handle, 0); ret == WAIT_OBJECT_0) {
::ResetEvent(m_handle);
return true;
} else {
return false;
}
}
void InterruptEventWindowsImpl::Clear() {
const auto ret = ::ResetEvent(m_handle);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
void InterruptEventWindowsImpl::Wait() {
while (true) {
/* Continuously wait, until success. */
auto ret = ::WaitForSingleObject(m_handle, INFINITE);
AMS_ASSERT(ret == WAIT_OBJECT_0);
AMS_UNUSED(ret);
/* If we're not obligated to clear, we're done. */
if (m_manual_clear) {
return;
}
/* Try to reset. */
if (this->ResetEventSignal()) {
return;
}
}
}
bool InterruptEventWindowsImpl::TryWait() {
/* If we're auto clear, just try to reset. */
if (!m_manual_clear) {
return this->ResetEventSignal();
}
const auto ret = ::WaitForSingleObject(m_handle, 0);
AMS_ASSERT((ret == WAIT_OBJECT_0) || (ret == WAIT_TIMEOUT));
return ret == WAIT_OBJECT_0;
}
bool InterruptEventWindowsImpl::TimedWait(TimeSpan timeout) {
TimeoutHelper timeout_helper(timeout);
do {
if (const auto res = ::WaitForSingleObject(m_handle, timeout_helper.GetTimeLeftOnTarget()); res == WAIT_OBJECT_0) {
/* If we're not obligated to clear, we're done. */
if (m_manual_clear) {
return true;
}
/* Try to reset. */
if (this->ResetEventSignal()) {
return true;
}
}
} while (!timeout_helper.TimedOut());
return false;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 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 InterruptEventWindowsImpl {
private:
NativeHandle m_handle;
bool m_manual_clear;
public:
explicit InterruptEventWindowsImpl(InterruptName name, EventClearMode mode);
~InterruptEventWindowsImpl();
void Clear();
void Wait();
bool TryWait();
bool TimedWait(TimeSpan timeout);
TriBool IsSignaled() {
return TriBool::Undefined;
}
NativeHandle GetHandle() const {
return m_handle;
}
private:
bool ResetEventSignal();
};
using InterruptEventTargetImpl = InterruptEventWindowsImpl;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 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_io_region_impl.hpp"
namespace ams::os::impl {
Result IoRegionImpl::CreateIoRegion(NativeHandle *out, NativeHandle io_pool_handle, uintptr_t address, size_t size, MemoryMapping mapping, MemoryPermission permission) {
AMS_UNUSED(out, io_pool_handle, address, size, mapping, permission);
R_THROW(os::ResultNotSupported());
}
Result IoRegionImpl::MapIoRegion(void **out, NativeHandle handle, size_t size, MemoryPermission perm) {
AMS_UNUSED(out, handle, size, perm);
R_THROW(os::ResultNotSupported());
}
void IoRegionImpl::UnmapIoRegion(NativeHandle handle, void *address, size_t size) {
AMS_UNUSED(handle, address, size);
}
}

View File

@@ -122,12 +122,16 @@ namespace ams::os::impl {
break;
default: /* 0 - 0x3F, valid. */
{
AMS_ASSERT(0 <= index && index < static_cast<s32>(MaximumHandleCount));
if constexpr (MaximumHandleCount > 0) {
AMS_ASSERT(0 <= index && index < static_cast<s32>(MaximumHandleCount));
std::scoped_lock lk(m_cs_wait);
m_signaled_holder = objects[index];
*out = objects[index];
return wait_result;
std::scoped_lock lk(m_cs_wait);
m_signaled_holder = objects[index];
*out = objects[index];
return wait_result;
} else {
AMS_ABORT_UNLESS(MaximumHandleCount > 0);
}
}
break;
}

View File

@@ -18,6 +18,12 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_multiple_wait_target_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_multiple_wait_target_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_multiple_wait_target_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_multiple_wait_target_impl.os.macos.hpp"
#else
#error "Unknown OS for ams::os::MultiWaitTargetImpl"
#endif

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 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_multiple_wait_holder_base.hpp"
#include "os_multiple_wait_impl.hpp"
#include "os_timeout_helper.hpp"
#include "os_inter_process_event_impl.os.linux.hpp"
#include <poll.h>
namespace ams::os::impl {
MultiWaitLinuxImpl::MultiWaitLinuxImpl() {
R_ABORT_UNLESS(InterProcessEventLinuxImpl::CreateSingle(std::addressof(m_cancel_event)));
}
MultiWaitLinuxImpl::~MultiWaitLinuxImpl() {
InterProcessEventLinuxImpl::Close(m_cancel_event);
m_cancel_event = InvalidNativeHandle;
}
void MultiWaitLinuxImpl::CancelWait() {
InterProcessEventLinuxImpl::Signal(m_cancel_event);
}
Result MultiWaitLinuxImpl::PollNativeHandlesImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns) {
/* Check that we can add our cancel handle to the wait. */
AMS_ABORT_UNLESS(array_size <= static_cast<s32>(MaximumHandleCount));
AMS_UNUSED(array_size);
/* Create poll fds. */
struct pollfd pfds[MaximumHandleCount + 1];
for (auto i = 0; i < num; ++i) {
pfds[i].fd = arr[i];
pfds[i].events = POLLIN;
pfds[i].revents = 0;
}
pfds[num].fd = m_cancel_event;
pfds[num].events = POLLIN;
pfds[num].revents = 0;
/* Determine timeout. */
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
struct timespec ts = { .tv_sec = (ns / NanoSecondsPerSecond), .tv_nsec = ns % NanoSecondsPerSecond };
/* Wait. */
while (true) {
const auto ret = ::ppoll(pfds, num + 1, ns >= 0 ? std::addressof(ts) : nullptr, nullptr);
if (ret < 0) {
/* Treat EINTR like a cancellation event; this will lead to a re-poll if nothing is signaled. */
AMS_ABORT_UNLESS(errno == EINTR);
*out_index = MultiWaitImpl::WaitCancelled;
R_SUCCEED();
}
/* Determine what event was polled. */
if (ret == 0) {
*out_index = MultiWaitImpl::WaitTimedOut;
R_SUCCEED();
} else if (pfds[num].revents != 0) {
*out_index = MultiWaitImpl::WaitCancelled;
/* Reset our cancel event. */
InterProcessEventLinuxImpl::Clear(m_cancel_event);
R_SUCCEED();
} else {
for (auto i = 0; i < num; ++i) {
if (pfds[i].revents != 0) {
*out_index = i;
R_SUCCEED();
}
}
AMS_ABORT("This should be impossible?");
}
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 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.hpp"
namespace ams::os::impl {
class MultiWaitLinuxImpl {
public:
/* TODO: This can potentially be higher. */
static constexpr size_t MaximumHandleCount = 64;
private:
NativeHandle m_cancel_event;
private:
Result PollNativeHandlesImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns);
Result ReplyAndReceiveImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns, NativeHandle reply_target);
public:
MultiWaitLinuxImpl();
~MultiWaitLinuxImpl();
void CancelWait();
Result WaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num) {
return this->PollNativeHandlesImpl(out_index, num, arr, array_size, static_cast<s64>(-1));
}
Result TryWaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num) {
return this->PollNativeHandlesImpl(out_index, num, arr, array_size, 0);
}
Result TimedWaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, TimeSpan ts) {
return this->PollNativeHandlesImpl(out_index, num, arr, array_size, ts.GetNanoSeconds());
}
Result ReplyAndReceive(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, NativeHandle reply_target) {
return this->ReplyAndReceiveImpl(out_index, num, arr, array_size, std::numeric_limits<s64>::max(), reply_target);
}
Result TimedReplyAndReceive(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, NativeHandle reply_target, TimeSpan ts) {
return this->ReplyAndReceiveImpl(out_index, num, arr, array_size, ts.GetNanoSeconds(), reply_target);
}
void SetCurrentThreadHandleForCancelWait() {
/* ... */
}
void ClearCurrentThreadHandleForCancelWait() {
/* ... */
}
};
using MultiWaitTargetImpl = MultiWaitLinuxImpl;
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 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_multiple_wait_holder_base.hpp"
#include "os_multiple_wait_impl.hpp"
#include "os_timeout_helper.hpp"
#include "os_inter_process_event_impl.os.macos.hpp"
#include <poll.h>
namespace ams::os::impl {
MultiWaitMacosImpl::MultiWaitMacosImpl() {
R_ABORT_UNLESS(InterProcessEventMacosImpl::Create(std::addressof(m_cancel_write_handle), std::addressof(m_cancel_read_handle)));
}
MultiWaitMacosImpl::~MultiWaitMacosImpl() {
InterProcessEventMacosImpl::Close(m_cancel_write_handle);
InterProcessEventMacosImpl::Close(m_cancel_read_handle);
m_cancel_write_handle = InvalidNativeHandle;
m_cancel_read_handle = InvalidNativeHandle;
}
void MultiWaitMacosImpl::CancelWait() {
InterProcessEventMacosImpl::Signal(m_cancel_write_handle);
}
Result MultiWaitMacosImpl::PollNativeHandlesImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns) {
/* Check that we can add our cancel handle to the wait. */
AMS_ABORT_UNLESS(array_size <= static_cast<s32>(MaximumHandleCount));
AMS_UNUSED(array_size);
/* Create poll fds. */
struct pollfd pfds[MaximumHandleCount + 1];
for (auto i = 0; i < num; ++i) {
pfds[i].fd = arr[i];
pfds[i].events = POLLIN;
pfds[i].revents = 0;
}
pfds[num].fd = m_cancel_read_handle;
pfds[num].events = POLLIN;
pfds[num].revents = 0;
/* Determine timeout. */
constexpr s64 NanoSecondsPerMilliSecond = TimeSpan::FromMilliSeconds(1).GetNanoSeconds();
/* TODO: Will macos ever support ppoll? */
const int timeout = static_cast<int>(ns >= 0 ? (ns / NanoSecondsPerMilliSecond) : -1);
/* Wait. */
while (true) {
const auto ret = ::poll(pfds, num + 1, timeout);
if (ret < 0) {
/* Treat EINTR like a cancellation event; this will lead to a re-poll if nothing is signaled. */
AMS_ABORT_UNLESS(errno == EINTR);
*out_index = MultiWaitImpl::WaitCancelled;
R_SUCCEED();
}
/* Determine what event was polled. */
if (ret == 0) {
*out_index = MultiWaitImpl::WaitTimedOut;
R_SUCCEED();
} else if (pfds[num].revents != 0) {
*out_index = MultiWaitImpl::WaitCancelled;
/* Reset our cancel event. */
InterProcessEventMacosImpl::Clear(m_cancel_read_handle);
R_SUCCEED();
} else {
for (auto i = 0; i < num; ++i) {
if (pfds[i].revents != 0) {
*out_index = i;
R_SUCCEED();
}
}
AMS_ABORT("This should be impossible?");
}
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 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.hpp"
namespace ams::os::impl {
class MultiWaitMacosImpl {
public:
/* TODO: This can potentially be higher. */
static constexpr size_t MaximumHandleCount = 64;
private:
NativeHandle m_cancel_read_handle;
NativeHandle m_cancel_write_handle;
private:
Result PollNativeHandlesImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns);
Result ReplyAndReceiveImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns, NativeHandle reply_target);
public:
MultiWaitMacosImpl();
~MultiWaitMacosImpl();
void CancelWait();
Result WaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num) {
return this->PollNativeHandlesImpl(out_index, num, arr, array_size, static_cast<s64>(-1));
}
Result TryWaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num) {
return this->PollNativeHandlesImpl(out_index, num, arr, array_size, 0);
}
Result TimedWaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, TimeSpan ts) {
return this->PollNativeHandlesImpl(out_index, num, arr, array_size, ts.GetNanoSeconds());
}
Result ReplyAndReceive(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, NativeHandle reply_target) {
return this->ReplyAndReceiveImpl(out_index, num, arr, array_size, std::numeric_limits<s64>::max(), reply_target);
}
Result TimedReplyAndReceive(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, NativeHandle reply_target, TimeSpan ts) {
return this->ReplyAndReceiveImpl(out_index, num, arr, array_size, ts.GetNanoSeconds(), reply_target);
}
void SetCurrentThreadHandleForCancelWait() {
/* ... */
}
void ClearCurrentThreadHandleForCancelWait() {
/* ... */
}
};
using MultiWaitTargetImpl = MultiWaitMacosImpl;
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 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_multiple_wait_holder_base.hpp"
#include "os_multiple_wait_impl.hpp"
#include "os_timeout_helper.hpp"
namespace ams::os::impl {
Result MultiWaitWindowsImpl::WaitForMultipleObjectsImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, DWORD wait_ms) {
/* Check that we can add our cancel handle to the wait. */
AMS_ABORT_UNLESS(num + 1 <= array_size);
AMS_UNUSED(array_size);
/* Add our cancel handle. */
arr[num] = m_cancel_event;
/* Wait. */
auto result = ::WaitForMultipleObjects(num + 1, arr, FALSE, wait_ms);
if (result == WAIT_TIMEOUT) {
*out_index = MultiWaitImpl::WaitTimedOut;
} else {
const s32 index = result - WAIT_OBJECT_0;
AMS_ASSERT((0 <= index) && (index <= num));
if (index == num) {
*out_index = MultiWaitImpl::WaitCancelled;
} else {
*out_index = index;
}
}
R_SUCCEED();
}
Result MultiWaitWindowsImpl::TimedWaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, TimeSpan ts) {
impl::TimeoutHelper timeout(ts);
do {
s32 idx;
R_TRY(WaitForMultipleObjectsImpl(std::addressof(idx), num, arr, array_size, timeout.GetTimeLeftOnTarget()));
if (idx != MultiWaitImpl::WaitTimedOut) {
*out_index = idx;
R_SUCCEED();
}
} while (!timeout.TimedOut());
*out_index = MultiWaitImpl::WaitTimedOut;
R_SUCCEED();
}
Result MultiWaitWindowsImpl::ReplyAndReceiveImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns, NativeHandle reply_target) {
AMS_UNUSED(out_index, num, arr, array_size, ns, reply_target);
R_ABORT_UNLESS(os::ResultNotImplemented());
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 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.hpp"
namespace ams::os::impl {
class MultiWaitWindowsImpl {
public:
static constexpr size_t MaximumHandleCount = static_cast<size_t>(MAXIMUM_WAIT_OBJECTS);
private:
os::NativeHandle m_cancel_event;
private:
Result WaitForMultipleObjectsImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, DWORD wait_ms);
Result ReplyAndReceiveImpl(s32 *out_index, s32 num, NativeHandle arr[], s32 array_size, s64 ns, NativeHandle reply_target);
public:
MultiWaitWindowsImpl() {
m_cancel_event = ::CreateEvent(nullptr, false, false, nullptr);
AMS_ASSERT(m_cancel_event != os::InvalidNativeHandle);
}
~MultiWaitWindowsImpl() {
const auto ret = ::CloseHandle(m_cancel_event);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
void CancelWait() {
::SetEvent(m_cancel_event);
}
Result WaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num) {
return this->WaitForMultipleObjectsImpl(out_index, num, arr, array_size, INFINITE);
}
Result TryWaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num) {
return this->WaitForMultipleObjectsImpl(out_index, num, arr, array_size, 0);
}
Result TimedWaitAny(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, TimeSpan ts);
Result ReplyAndReceive(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, NativeHandle reply_target) {
return this->ReplyAndReceiveImpl(out_index, num, arr, array_size, std::numeric_limits<s64>::max(), reply_target);
}
Result TimedReplyAndReceive(s32 *out_index, NativeHandle arr[], s32 array_size, s32 num, NativeHandle reply_target, TimeSpan ts) {
return this->ReplyAndReceiveImpl(out_index, num, arr, array_size, ts.GetNanoSeconds(), reply_target);
}
void SetCurrentThreadHandleForCancelWait() {
/* ... */
}
void ClearCurrentThreadHandleForCancelWait() {
/* ... */
}
};
using MultiWaitTargetImpl = MultiWaitWindowsImpl;
}

View File

@@ -18,6 +18,12 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_native_handle_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_native_handle_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_native_handle_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_native_handle_impl.os.macos.hpp"
#else
#error "Unknown OS for ams::os::NativeHandleImpl"
#endif

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 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 <unistd.h>
namespace ams::os::impl {
class NativeHandleLinuxImpl {
public:
static ALWAYS_INLINE void Close(NativeHandle handle) {
s32 ret;
do {
ret = ::close(handle);
} while (ret < 0 && errno == EINTR);
AMS_ASSERT(ret == 0);
}
};
using NativeHandleImpl = NativeHandleLinuxImpl;
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 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 <unistd.h>
namespace ams::os::impl {
class NativeHandleMacosImpl {
public:
static ALWAYS_INLINE void Close(NativeHandle handle) {
s32 ret;
do {
ret = ::close(handle);
} while (ret < 0 && errno == EINTR);
AMS_ASSERT(ret == 0);
}
};
using NativeHandleImpl = NativeHandleMacosImpl;
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 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 NativeHandleWindowsImpl {
public:
static ALWAYS_INLINE void Close(NativeHandle handle) {
const auto ret = ::CloseHandle(handle);
AMS_ASSERT(ret != 0);
AMS_UNUSED(ret);
}
};
using NativeHandleImpl = NativeHandleWindowsImpl;
}

View File

@@ -18,6 +18,12 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_process_handle_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_process_handle_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_process_handle_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_process_handle_impl.os.macos.hpp"
#else
#error "Unknown OS for ams::os::ProcessHandleImpl"
#endif

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 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 ProcessHandleLinuxImpl {
public:
static consteval NativeHandle GetCurrentProcessHandle() {
/* TODO: PseudoHandle define? */
return -2;
}
static ALWAYS_INLINE Result GetProcessId(ProcessId *out, NativeHandle handle) {
AMS_UNUSED(out, handle);
AMS_ABORT("TODO");
}
static ALWAYS_INLINE Result GetProgramId(ncm::ProgramId *out, NativeHandle handle) {
AMS_UNUSED(out, handle);
AMS_ABORT("TODO");
}
};
using ProcessHandleImpl = ProcessHandleLinuxImpl;
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 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 ProcessHandleMacosImpl {
public:
static consteval NativeHandle GetCurrentProcessHandle() {
/* TODO: PseudoHandle define? */
return -2;
}
static ALWAYS_INLINE Result GetProcessId(ProcessId *out, NativeHandle handle) {
AMS_UNUSED(out, handle);
AMS_ABORT("TODO");
}
static ALWAYS_INLINE Result GetProgramId(ncm::ProgramId *out, NativeHandle handle) {
AMS_UNUSED(out, handle);
AMS_ABORT("TODO");
}
};
using ProcessHandleImpl = ProcessHandleMacosImpl;
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 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 ProcessHandleWindowsImpl {
public:
static ALWAYS_INLINE NativeHandle GetCurrentProcessHandle() {
return ::GetCurrentProcess();
}
static ALWAYS_INLINE Result GetProcessId(ProcessId *out, NativeHandle handle) {
AMS_UNUSED(out, handle);
AMS_ABORT("TODO");
}
static ALWAYS_INLINE Result GetProgramId(ncm::ProgramId *out, NativeHandle handle) {
AMS_UNUSED(out, handle);
AMS_ABORT("TODO");
}
};
using ProcessHandleImpl = ProcessHandleWindowsImpl;
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 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>
namespace ams::os::impl {
void InitializeRandomImpl(util::TinyMT *mt) {
/* Initialize twister based on system tick. */
mt->Initialize(os::GetSystemTick().GetInt64Value());
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 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>
namespace ams::os::impl {
void InitializeRandomImpl(util::TinyMT *mt) {
/* Initialize twister based on system tick. */
mt->Initialize(os::GetSystemTick().GetInt64Value());
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 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>
namespace ams::os::impl {
void InitializeRandomImpl(util::TinyMT *mt) {
/* Initialize twister based on system tick. */
mt->Initialize(os::GetSystemTick().GetInt64Value());
}
}

View File

@@ -19,6 +19,9 @@
#include "os_thread_manager_types.hpp"
#include "os_tick_manager_impl.hpp"
#include "os_aslr_space_manager_types.hpp"
#include "os_tls_manager_types.hpp"
#include "os_giant_lock_types.hpp"
#include "os_vamm_manager_types.hpp"
namespace ams::os::impl {
@@ -28,21 +31,26 @@ namespace ams::os::impl {
AslrSpaceManager m_aslr_space_manager{};
/* TODO */
ThreadManager m_thread_manager{};
/* TODO */
//TlsManager m_tls_manager{};
TickManager m_tick_manager{};
/* TODO */
VammManager m_vamm_manager;
GiantLock m_giant_lock{};
public:
OsResourceManager() = default;
constexpr ALWAYS_INLINE RngManager &GetRngManager() { return m_rng_manager; }
constexpr ALWAYS_INLINE AslrSpaceManager &GetAslrSpaceManager() { return m_aslr_space_manager; }
constexpr ALWAYS_INLINE ThreadManager &GetThreadManager() { return m_thread_manager; }
//constexpr ALWAYS_INLINE TlsManager &GetTlsManager() { return m_tls_manager; }
constexpr ALWAYS_INLINE TickManager &GetTickManager() { return m_tick_manager; }
constexpr ALWAYS_INLINE VammManager &GetVammManager() { return m_vamm_manager; }
constexpr ALWAYS_INLINE GiantLock &GetGiantLock() { return m_giant_lock; }
};
class ResourceManagerHolder {
private:
static util::TypedStorage<OsResourceManager> s_resource_manager_storage;
static constinit util::TypedStorage<OsResourceManager> s_resource_manager_storage;
private:
constexpr ResourceManagerHolder() { /* ... */ }
public:

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 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_rng_manager_impl.hpp"
namespace ams::os::impl {
void RngManager::Initialize() {
/* Initialize twister based on system tick. */
m_mt.Initialize(os::GetSystemTick().GetInt64Value());
/* Note that we've initialized. */
m_initialized = true;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 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_rng_manager_impl.hpp"
namespace ams::os::impl {
void RngManager::Initialize() {
/* Initialize twister based on system tick. */
m_mt.Initialize(os::GetSystemTick().GetInt64Value());
/* Note that we've initialized. */
m_initialized = true;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 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_rng_manager_impl.hpp"
namespace ams::os::impl {
void RngManager::Initialize() {
/* Initialize twister based on system tick. */
m_mt.Initialize(os::GetSystemTick().GetInt64Value());
/* Note that we've initialized. */
m_initialized = true;
}
}

View File

@@ -19,7 +19,7 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_rw_lock_target_impl.os.horizon.hpp"
#else
#error "Unknown OS for os::ReaderWriterLockTargetImpl"
#include "os_rw_lock_target_impl.os.generic.hpp"
#endif
namespace ams::os::impl {

View File

@@ -0,0 +1,238 @@
/*
* Copyright (c) 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_rw_lock_impl.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
void ReaderWriterLockHorizonImpl::AcquireReadLock(os::ReaderWriterLockType *rw_lock) {
/* Acquire exclusive access. */
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
/* Get the lock into a state where we can safely increment the read lock count. */
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
/* If we're the owner thread, we should hold the write lock. */
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
} else {
/* Wait until we're not write locked, and there are no write lock waiters. */
while (true) {
/* Check if we're write locked. */
if (GetWriteLocked(GetLockCount(rw_lock)) != 1) {
/* We're not, so check if we have write lock waiters. */
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) == 0) {
break;
}
}
/* We're write locked, or we have write lock waiters. */
IncReadLockWaiterCount(GetLockCount(rw_lock));
util::GetReference(rw_lock->cv_read_lock._storage).Wait(util::GetPointer(GetLockCount(rw_lock).cs_storage));
DecReadLockWaiterCount(GetLockCount(rw_lock));
}
/* Verify we're in the desired state. */
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
AMS_ASSERT(rw_lock->owner_thread == nullptr);
}
/* Increment the read lock count. */
IncReadLockCount(GetLockCount(rw_lock));
}
bool ReaderWriterLockHorizonImpl::TryAcquireReadLock(os::ReaderWriterLockType *rw_lock) {
/* Acquire exclusive access. */
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
/* Try to get the lock into a state where we can safely increment the read lock count. */
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
/* If we're the owner thread, we should hold the write lock. */
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
} else {
/* We can only read lock if we're not write locked, and there are no write lock waiters. */
if (GetWriteLocked(GetLockCount(rw_lock)) == 1) {
return false;
}
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
return false;
}
/* Verify we're in the desired state. */
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
AMS_ASSERT(rw_lock->owner_thread == nullptr);
}
/* Increment the read lock count. */
IncReadLockCount(GetLockCount(rw_lock));
return true;
}
void ReaderWriterLockHorizonImpl::ReleaseReadLock(os::ReaderWriterLockType *rw_lock) {
/* Acquire exclusive access. */
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
/* Sanity check that we hold the read lock. */
AMS_ASSERT(GetReadLockCount(GetLockCount(rw_lock)) > 0);
/* Decrement the read lock count. */
DecReadLockCount(GetLockCount(rw_lock));
/* If we're the owner of the write lock, we may need to check if this causes a full release/signal to waiters. */
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
/* Sanity check that we're write locked. */
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
/* If we've called ReleaseWriteLock and have no remaining read locks, we may need to signal/broadcast. */
if (GetWriteLockCount(*rw_lock) == 0 && GetReadLockCount(GetLockCount(rw_lock)) == 0) {
/* Clear lock owner. */
rw_lock->owner_thread = nullptr;
/* Clear write locked. */
ClearWriteLocked(GetLockCount(rw_lock));
/* If we have lock waiters, signal them. */
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
util::GetReference(rw_lock->cv_write_lock._storage).Signal();
} else if (GetReadLockWaiterCount(GetLockCount(rw_lock)) != 0) {
util::GetReference(rw_lock->cv_read_lock._storage).Broadcast();
}
}
} else {
/* Sanity check that our read lock release was fine. */
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 0);
AMS_ASSERT(rw_lock->owner_thread == nullptr);
/* We need to signal if there are no remaining read locks. */
if (GetReadLockCount(GetLockCount(rw_lock)) == 0) {
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
util::GetReference(rw_lock->cv_write_lock._storage).Signal();
}
}
}
}
void ReaderWriterLockHorizonImpl::AcquireWriteLock(os::ReaderWriterLockType *rw_lock) {
/* Acquire exclusive access. */
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
/* Get the lock into a state where we can safely increment the read lock count. */
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
/* If we're the owner thread, we should hold the write lock. */
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
/* Increment the write lock count. */
IncWriteLockCount(*rw_lock);
} else {
/* Wait until we're not read locked and not write locked. */
while (true) {
/* Check if we're read locked. */
if (GetReadLockCount(GetLockCount(rw_lock)) == 0) {
/* We're not, so check if we're write locked. */
if (GetWriteLocked(GetLockCount(rw_lock)) != 1) {
break;
}
}
/* We're write locked or read locked. */
IncWriteLockWaiterCount(GetLockCount(rw_lock));
util::GetReference(rw_lock->cv_write_lock._storage).Wait(util::GetPointer(GetLockCount(rw_lock).cs_storage));
DecWriteLockWaiterCount(GetLockCount(rw_lock));
}
/* Verify we're in the desired state. */
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
AMS_ASSERT(rw_lock->owner_thread == nullptr);
/* Increment the write lock count. */
IncWriteLockCount(*rw_lock);
/* Set write locked. */
SetWriteLocked(GetLockCount(rw_lock));
/* Set ourselves as the owner. */
rw_lock->owner_thread = impl::GetCurrentThread();
}
}
bool ReaderWriterLockHorizonImpl::TryAcquireWriteLock(os::ReaderWriterLockType *rw_lock) {
/* Acquire exclusive access. */
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
/* Get the lock into a state where we can safely increment the read lock count. */
if (rw_lock->owner_thread == impl::GetCurrentThread()) {
/* If we're the owner thread, we should hold the write lock. */
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) == 1);
/* Increment the write lock count. */
IncWriteLockCount(*rw_lock);
} else {
/* Check if we're read locked. */
if (GetReadLockCount(GetLockCount(rw_lock)) != 0) {
return false;
}
/* We're not, so check if we're write locked. */
if (GetWriteLocked(GetLockCount(rw_lock)) == 1) {
return false;
}
/* Verify we're in the desired state. */
AMS_ASSERT(GetWriteLockCount(*rw_lock) == 0);
AMS_ASSERT(rw_lock->owner_thread == nullptr);
/* Increment the write lock count. */
IncWriteLockCount(*rw_lock);
/* Set write locked. */
SetWriteLocked(GetLockCount(rw_lock));
/* Set ourselves as the owner. */
rw_lock->owner_thread = impl::GetCurrentThread();
}
return true;
}
void ReaderWriterLockHorizonImpl::ReleaseWriteLock(os::ReaderWriterLockType *rw_lock) {
/* Acquire exclusive access. */
std::scoped_lock lk(util::GetReference(GetLockCount(rw_lock).cs_storage));
/* Sanity check our state. */
AMS_ASSERT(GetWriteLockCount(*rw_lock) > 0);
AMS_ASSERT(GetWriteLocked(GetLockCount(rw_lock)) != 0);
AMS_ASSERT(rw_lock->owner_thread == impl::GetCurrentThread());
/* Decrement the write lock count. */
DecWriteLockCount(*rw_lock);
/* If we have no remaining write locks, we may need to signal/release. */
if (GetWriteLockCount(*rw_lock) == 0 && GetReadLockCount(GetLockCount(rw_lock)) == 0) {
/* Clear lock owner. */
rw_lock->owner_thread = nullptr;
/* Clear write locked. */
ClearWriteLocked(GetLockCount(rw_lock));
/* If we have lock waiters, signal them. */
if (GetWriteLockWaiterCount(GetLockCount(rw_lock)) != 0) {
util::GetReference(rw_lock->cv_write_lock._storage).Signal();
} else if (GetReadLockWaiterCount(GetLockCount(rw_lock)) != 0) {
util::GetReference(rw_lock->cv_read_lock._storage).Broadcast();
}
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 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 ReaderWriterLockHorizonImpl {
private:
using LockCount = os::ReaderWriterLockType::LockCount;
public:
static void AcquireReadLock(os::ReaderWriterLockType *rw_lock);
static bool TryAcquireReadLock(os::ReaderWriterLockType *rw_lock);
static void ReleaseReadLock(os::ReaderWriterLockType *rw_lock);
static void AcquireWriteLock(os::ReaderWriterLockType *rw_lock);
static bool TryAcquireWriteLock(os::ReaderWriterLockType *rw_lock);
static void ReleaseWriteLock(os::ReaderWriterLockType *rw_lock);
};
using ReaderWriterLockTargetImpl = ReaderWriterLockHorizonImpl;
}

View File

@@ -23,7 +23,10 @@
namespace ams::os::impl {
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority) {
void SetupThreadObjectUnsafe(ThreadType *thread, void *platform, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority) {
/* Clear the thread object. */
std::memset(thread, 0, sizeof(*thread));
/* Setup objects. */
util::ConstructAt(thread->cs_thread);
util::ConstructAt(thread->cv_thread);
@@ -32,20 +35,37 @@ namespace ams::os::impl {
util::ConstructAt(thread->waitlist);
/* Set member variables. */
thread->magic = os::ThreadType::Magic;
thread->version = 0;
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;
thread->magic = os::ThreadType::Magic;
thread->stack_is_aliased = false;
thread->auto_registered = false;
thread->version = 0;
thread->function = function;
thread->argument = arg;
thread->original_stack = stack;
thread->stack = stack;
thread->stack_size = stack_size;
thread->base_priority = priority;
thread->suspend_count = 0;
thread->initial_fiber = nullptr;
thread->current_fiber = nullptr;
thread->name_buffer[0] = '\x00';
thread->name_pointer = thread->name_buffer;
/* Set internal tls variables. */
thread->atomic_sf_inline_context = 0;
/* Set platform variables. */
#if defined(AMS_OS_IMPL_USE_PTHREADS)
util::ConstructAt(thread->cs_pthread_exit);
util::ConstructAt(thread->cv_pthread_exit);
thread->exited_pthread = false;
std::memset(thread->tls_value_array, 0, sizeof(thread->tls_value_array));
AMS_UNUSED(platform);
#elif defined(ATMOSPHERE_OS_HORIZON)
std::memset(std::addressof(thread->sdk_internal_tls), 0, sizeof(thread->sdk_internal_tls));
thread->thread_impl = (platform != nullptr) ? static_cast<ThreadType::ThreadImpl *>(platform) : std::addressof(thread->thread_impl_storage);
#else
std::memset(thread->tls_value_array, 0, sizeof(thread->tls_value_array));
AMS_UNUSED(platform);
#endif
/* Mark initialized. */
thread->state = ThreadType::State_Initialized;
@@ -74,36 +94,61 @@ namespace ams::os::impl {
}
ThreadManager::ThreadManager() : m_impl(std::addressof(m_main_thread)), m_total_thread_stack_size(0), m_num_created_threads(0) {
m_total_thread_stack_size = 0;
m_num_created_threads = 0;
m_main_thread.state = ThreadType::State_Started;
this->SetCurrentThread(std::addressof(m_main_thread));
this->PlaceThreadObjectUnderThreadManagerSafe(std::addressof(m_main_thread));
}
void ThreadManager::CleanupThread(ThreadType *thread) {
/* TODO: TLS Manager->InvokeTlsDestructors(); */
std::scoped_lock lk(GetReference(thread->cs_thread));
thread->state = ThreadType::State_Terminated;
GetReference(thread->cv_thread).Broadcast();
GetReference(thread->waitlist).SignalAllThreads();
}
void ThreadManager::CleanupThread() {
ThreadType *thread = this->GetCurrentThread();
return this->CleanupThread(this->GetCurrentThread());
}
{
std::scoped_lock lk(GetReference(thread->cs_thread));
thread->state = ThreadType::State_Terminated;
GetReference(thread->cv_thread).Broadcast();
GetReference(thread->waitlist).SignalAllThreads();
bool ThreadManager::CreateAliasStackUnsafe(ThreadType *thread) {
void *alias_stack;
if (m_impl.MapAliasStack(std::addressof(alias_stack), thread->stack, thread->stack_size)) {
thread->stack_is_aliased = true;
thread->stack = alias_stack;
return true;
} else {
return false;
}
}
void ThreadManager::DeleteAliasStackUnsafe(ThreadType *thread) {
AMS_ABORT_UNLESS(m_impl.UnmapAliasStack(thread->stack, thread->original_stack, thread->stack_size));
thread->stack_is_aliased = false;
thread->stack = thread->original_stack;
}
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);
AMS_ABORT_UNLESS(this->CreateAliasStackUnsafe(thread));
ON_RESULT_FAILURE {
this->DeleteAliasStackUnsafe(thread);
thread->state = ThreadType::State_NotInitialized;
};
auto guard = SCOPE_GUARD { thread->state = ThreadType::State_NotInitialized; };
R_TRY(m_impl.CreateThread(thread, ideal_core));
guard.Cancel();
this->PlaceThreadObjectUnderThreadManagerSafe(thread);
return ResultSuccess();
R_SUCCEED();
}
Result ThreadManager::CreateThread(ThreadType *thread, ThreadFunction function, void *argument, void *stack, size_t stack_size, s32 priority) {
@@ -123,27 +168,33 @@ namespace ams::os::impl {
m_impl.WaitForThreadExit(thread);
AMS_ASSERT(thread->state == ThreadType::State_Initialized);
this->DestroyThreadObject(thread);
}
void ThreadManager::DestroyThreadObject(ThreadType *thread) {
{
std::scoped_lock lk(GetReference(thread->cs_thread));
/* NOTE: Here Nintendo would cleanup the alias stack. */
m_impl.DestroyThreadUnsafe(thread);
thread->state = ThreadType::State_NotInitialized;
util::DestroyAt(thread->waitlist);
thread->name_buffer[0] = '\x00';
thread->magic = 0xCCCC;
{
std::scoped_lock tlk(m_cs);
this->EraseFromAllThreadsListUnsafe(thread);
}
if (thread->stack_is_aliased) {
this->DeleteAliasStackUnsafe(thread);
}
m_impl.DestroyThreadUnsafe(thread);
thread->state = ThreadType::State_NotInitialized;
thread->name_buffer[0] = '\x00';
thread->magic = 0xCCCC;
util::DestroyAt(thread->waitlist);
}
util::DestroyAt(thread->cs_thread);
util::DestroyAt(thread->cv_thread);
}
void ThreadManager::StartThread(ThreadType *thread) {
@@ -163,7 +214,9 @@ namespace ams::os::impl {
{
std::scoped_lock lk(GetReference(thread->cs_thread));
/* Note: Here Nintendo would cleanup the alias stack. */
if (thread->stack_is_aliased) {
this->DeleteAliasStackUnsafe(thread);
}
}
}
@@ -173,7 +226,9 @@ namespace ams::os::impl {
if (result) {
std::scoped_lock lk(GetReference(thread->cs_thread));
/* Note: Here Nintendo would cleanup the alias stack. */
if (thread->stack_is_aliased) {
this->DeleteAliasStackUnsafe(thread);
}
}
return result;
@@ -182,7 +237,7 @@ namespace ams::os::impl {
s32 ThreadManager::SuspendThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
auto prev_suspend_count = thread->suspend_count;
const auto prev_suspend_count = thread->suspend_count;
AMS_ASSERT(prev_suspend_count < ThreadSuspendCountMax);
thread->suspend_count = prev_suspend_count + 1;
@@ -195,7 +250,7 @@ namespace ams::os::impl {
s32 ThreadManager::ResumeThread(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
auto prev_suspend_count = thread->suspend_count;
const auto prev_suspend_count = thread->suspend_count;
if (prev_suspend_count > 0) {
thread->suspend_count = prev_suspend_count - 1;
if (prev_suspend_count == 1) {
@@ -205,11 +260,15 @@ namespace ams::os::impl {
return prev_suspend_count;
}
void ThreadManager::CancelThreadSynchronization(ThreadType *thread) {
std::scoped_lock lk(GetReference(thread->cs_thread));
#if !defined(ATMOSPHERE_OS_HORIZON)
void ThreadManager::SetZeroToAllThreadsTlsSafe(int slot) {
std::scoped_lock lk(m_cs);
m_impl.CancelThreadSynchronizationUnsafe(thread);
for (auto it = m_all_threads_list.begin(); it != m_all_threads_list.end(); ++it) {
it->tls_value_array[slot] = 0;
}
}
#endif
/* TODO void ThreadManager::GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */

View File

@@ -30,11 +30,16 @@ namespace ams::os::impl {
return GetThreadManager().GetCurrentThread();
}
#if !defined(AMS_OS_IMPL_USE_PTHREADS)
ALWAYS_INLINE NativeHandle GetCurrentThreadHandle() {
/* return GetCurrentThread()->thread_impl->handle; */
#if defined(ATMOSPHERE_OS_HORIZON)
return ::threadGetCurHandle();
#else
return GetCurrentThread()->native_handle;
#endif
}
#endif
void SetupThreadObjectUnsafe(ThreadType *thread, ThreadImpl *thread_impl, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority);
void SetupThreadObjectUnsafe(ThreadType *thread, void *platform, ThreadFunction function, void *arg, void *stack, size_t stack_size, s32 priority);
}

View File

@@ -50,14 +50,15 @@ namespace ams::os::impl {
ThreadManagerHorizonImpl::ThreadManagerHorizonImpl(ThreadType *main_thread) {
/* Get the thread impl object from libnx. */
ThreadImpl *thread_impl = ::threadGetSelf();
ThreadType::ThreadImpl *thread_impl = ::threadGetSelf();
auto * const original_thread_impl = thread_impl;
/* Hack around libnx's main thread, to ensure stratosphere thread type consistency. */
{
auto *tlr = reinterpret_cast<uintptr_t *>(svc::GetThreadLocalRegion());
for (size_t i = sizeof(svc::ThreadLocalRegion) / sizeof(uintptr_t); i > 0; --i) {
if (auto *candidate = reinterpret_cast<ThreadImpl *>(tlr[i - 1]); candidate == thread_impl) {
ThreadImpl *embedded_thread = std::addressof(main_thread->thread_impl_storage);
if (auto *candidate = reinterpret_cast<ThreadType::ThreadImpl *>(tlr[i - 1]); candidate == thread_impl) {
ThreadType::ThreadImpl *embedded_thread = std::addressof(main_thread->thread_impl_storage);
*embedded_thread = *thread_impl;
@@ -78,6 +79,9 @@ namespace ams::os::impl {
SetupThreadObjectUnsafe(main_thread, thread_impl, nullptr, nullptr, thread_impl->stack_mirror, thread_impl->stack_sz, ConvertToUserPriority(horizon_priority));
/* Fix up the thread impl. */
*thread_impl = *original_thread_impl;
/* Set the thread id. */
u64 thread_id;
R_ABORT_UNLESS(svc::GetThreadId(std::addressof(thread_id), thread_impl->handle));
@@ -184,10 +188,6 @@ namespace ams::os::impl {
R_ABORT_UNLESS(svc::SetThreadActivity(thread->thread_impl->handle, svc::ThreadActivity_Runnable));
}
void ThreadManagerHorizonImpl::CancelThreadSynchronizationUnsafe(ThreadType *thread) {
R_ABORT_UNLESS(svc::CancelSynchronization(thread->thread_impl->handle));
}
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
s32 ThreadManagerHorizonImpl::GetCurrentCoreNumber() const {
@@ -218,5 +218,18 @@ namespace ams::os::impl {
return core_mask;
}
bool ThreadManagerHorizonImpl::MapAliasStack(void **out, void *stack, size_t size) {
/* TODO: This will need to be real, if we ever stop using libnx threads. */
AMS_UNUSED(stack, size);
*out = stack;
return true;
}
bool ThreadManagerHorizonImpl::UnmapAliasStack(void *alias_stack, void *original_stack, size_t size) {
/* TODO: This will need to be real, if we ever stop using libnx threads. */
AMS_UNUSED(alias_stack, original_stack, size);
return true;
}
}

View File

@@ -47,8 +47,6 @@ namespace ams::os::impl {
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 { AMS_UNUSED(thread); }
@@ -68,10 +66,17 @@ namespace ams::os::impl {
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const;
u64 GetThreadAvailableCoreMask() const;
bool MapAliasStack(void **out, void *stack, size_t size);
bool UnmapAliasStack(void *alias_stack, void *original_stack, size_t size);
NORETURN void ExitProcessImpl() {
svc::ExitProcess();
AMS_ABORT("Process was exited");
}
NORETURN void QuickExit() {
AMS_ABORT("TODO: make QuickExit properly a thing");
}
};
using ThreadManagerImpl = ThreadManagerHorizonImpl;

View File

@@ -0,0 +1,262 @@
/*
* Copyright (c) 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.windows.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
namespace {
constexpr size_t DefaultStackSize = 1_MB;
class ScopedSaveLastError {
NON_COPYABLE(ScopedSaveLastError);
NON_MOVEABLE(ScopedSaveLastError);
private:
DWORD m_last_error;
public:
ALWAYS_INLINE ScopedSaveLastError() : m_last_error(::GetLastError()) { /* ... */ }
ALWAYS_INLINE ~ScopedSaveLastError() { ::SetLastError(m_last_error); }
};
unsigned __stdcall InvokeThread(void *arg) {
ThreadType *thread = static_cast<ThreadType *>(arg);
/* Invoke the thread. */
ThreadManager::InvokeThread(thread);
return 0;
}
os::NativeHandle DuplicateThreadHandle(os::NativeHandle thread_handle) {
/* Get the thread's windows handle. */
os::NativeHandle windows_handle = os::InvalidNativeHandle;
AMS_ABORT_UNLESS(::DuplicateHandle(::GetCurrentProcess(), thread_handle, ::GetCurrentProcess(), std::addressof(windows_handle), 0, 0, DUPLICATE_SAME_ACCESS));
return windows_handle;
}
os::ThreadType *DynamicAllocateAndRegisterThreadType() {
/* Get the thread manager. */
auto &thread_manager = GetThreadManager();
/* Allocate a thread. */
auto *thread = thread_manager.AllocateThreadType();
AMS_ABORT_UNLESS(thread != nullptr);
/* Setup the thread object. */
SetupThreadObjectUnsafe(thread, nullptr, nullptr, nullptr, nullptr, 0, DefaultThreadPriority);
thread->state = ThreadType::State_Started;
thread->auto_registered = true;
/* Set the thread's windows handle. */
thread->native_handle = DuplicateThreadHandle(::GetCurrentThread());
thread->ideal_core = ::GetCurrentProcessorNumber();
thread->affinity_mask = thread_manager.GetThreadAvailableCoreMask();
/* Place the object under the thread manager. */
thread_manager.PlaceThreadObjectUnderThreadManagerSafe(thread);
return thread;
}
}
ThreadManagerWindowsImpl::ThreadManagerWindowsImpl(ThreadType *main_thread) : m_tls_index(::TlsAlloc()) {
/* Verify tls index is valid. */
AMS_ASSERT(m_tls_index != TLS_OUT_OF_INDEXES);
/* Setup the main thread object. */
SetupThreadObjectUnsafe(main_thread, nullptr, nullptr, nullptr, nullptr, DefaultStackSize, DefaultThreadPriority);
/* Setup the main thread's windows handle information. */
main_thread->native_handle = DuplicateThreadHandle(::GetCurrentThread());
main_thread->ideal_core = ::GetCurrentProcessorNumber();
main_thread->affinity_mask = this->GetThreadAvailableCoreMask();
}
Result ThreadManagerWindowsImpl::CreateThread(ThreadType *thread, s32 ideal_core) {
/* Create the thread. */
os::NativeHandle thread_handle = reinterpret_cast<os::NativeHandle>(_beginthreadex(nullptr, thread->stack_size, &InvokeThread, thread, CREATE_SUSPENDED, 0));
AMS_ASSERT(thread_handle != os::InvalidNativeHandle);
/* Set the thread's windows handle information. */
thread->native_handle = thread_handle;
thread->ideal_core = ideal_core;
thread->affinity_mask = this->GetThreadAvailableCoreMask();
R_SUCCEED();
}
void ThreadManagerWindowsImpl::DestroyThreadUnsafe(ThreadType *thread) {
/* Close the thread's handle. */
const auto ret = ::CloseHandle(thread->native_handle);
AMS_ASSERT(ret);
AMS_UNUSED(ret);
thread->native_handle = os::InvalidNativeHandle;
}
void ThreadManagerWindowsImpl::StartThread(const ThreadType *thread) {
ScopedSaveLastError save_error;
/* Resume the thread. */
const auto ret = ::ResumeThread(thread->native_handle);
AMS_ASSERT(ret == 1);
AMS_UNUSED(ret);
}
void ThreadManagerWindowsImpl::WaitForThreadExit(ThreadType *thread) {
::WaitForSingleObject(thread->native_handle, INFINITE);
}
bool ThreadManagerWindowsImpl::TryWaitForThreadExit(ThreadType *thread) {
return ::WaitForSingleObject(thread->native_handle, 0) == 0;
}
void ThreadManagerWindowsImpl::YieldThread() {
::Sleep(0);
}
bool ThreadManagerWindowsImpl::ChangePriority(ThreadType *thread, s32 priority) {
AMS_UNUSED(thread, priority);
return true;
}
s32 ThreadManagerWindowsImpl::GetCurrentPriority(const ThreadType *thread) const {
return thread->base_priority;
}
ThreadId ThreadManagerWindowsImpl::GetThreadId(const ThreadType *thread) const {
ScopedSaveLastError save_error;
const auto thread_id = ::GetThreadId(thread->native_handle);
AMS_ASSERT(thread_id != 0);
return thread_id;
}
void ThreadManagerWindowsImpl::SuspendThreadUnsafe(ThreadType *thread) {
ScopedSaveLastError save_error;
const auto ret = ::SuspendThread(thread->native_handle);
AMS_ASSERT(ret == 0);
AMS_UNUSED(ret);
}
void ThreadManagerWindowsImpl::ResumeThreadUnsafe(ThreadType *thread) {
ScopedSaveLastError save_error;
const auto ret = ::ResumeThread(thread->native_handle);
AMS_ASSERT(ret == 1);
AMS_UNUSED(ret);
}
/* TODO: void ThreadManagerWindowsImpl::GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
void ThreadManagerWindowsImpl::NotifyThreadNameChangedImpl(const ThreadType *thread) const {
/* TODO */
AMS_UNUSED(thread);
}
void ThreadManagerWindowsImpl::SetCurrentThread(ThreadType *thread) const {
ScopedSaveLastError save_error;
::TlsSetValue(m_tls_index, thread);
}
ThreadType *ThreadManagerWindowsImpl::GetCurrentThread() const {
ScopedSaveLastError save_error;
/* Get the thread from tls index. */
ThreadType *thread = static_cast<ThreadType *>(static_cast<void *>(::TlsGetValue(m_tls_index)));
/* If the thread's TLS isn't set, we need to find it (and set tls) or make it. */
if (thread == nullptr) {
/* Try to find the thread. */
thread = GetThreadManager().FindThreadTypeById(::GetCurrentThreadId());
if (thread == nullptr) {
/* Create the thread. */
thread = DynamicAllocateAndRegisterThreadType();
}
/* Set the thread's TLS. */
this->SetCurrentThread(thread);
}
return thread;
}
s32 ThreadManagerWindowsImpl::GetCurrentCoreNumber() const {
ScopedSaveLastError save_error;
return ::GetCurrentProcessorNumber();
}
void ThreadManagerWindowsImpl::SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const {
ScopedSaveLastError save_error;
/* If we should use the default, set the actual ideal core. */
if (ideal_core == IdealCoreUseDefault) {
affinity_mask = this->GetThreadAvailableCoreMask();
ideal_core = util::CountTrailingZeros(affinity_mask);
affinity_mask = static_cast<decltype(affinity_mask)>(1) << ideal_core;
}
/* Lock the thread. */
std::scoped_lock lk(util::GetReference(thread->cs_thread));
/* Set the thread's affinity mask. */
const auto old = ::SetThreadAffinityMask(thread->native_handle, affinity_mask);
AMS_ABORT_UNLESS(old != 0);
/* Set the ideal core. */
if (ideal_core != IdealCoreNoUpdate) {
thread->ideal_core = ideal_core;
}
/* Set the tracked affinity mask. */
thread->affinity_mask = affinity_mask;
}
void ThreadManagerWindowsImpl::GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const {
ScopedSaveLastError save_error;
/* Lock the thread. */
std::scoped_lock lk(util::GetReference(thread->cs_thread));
/* Set the output. */
if (out_ideal_core != nullptr) {
*out_ideal_core = thread->ideal_core;
}
if (out_affinity_mask != nullptr) {
*out_affinity_mask = thread->affinity_mask;
}
}
u64 ThreadManagerWindowsImpl::GetThreadAvailableCoreMask() const {
ScopedSaveLastError save_error;
/* Get the process's affinity mask. */
u64 process_affinity, system_affinity;
AMS_ABORT_UNLESS(::GetProcessAffinityMask(::GetCurrentProcess(), std::addressof(process_affinity), std::addressof(system_affinity)) != 0);
return process_affinity;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 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 ThreadManagerWindowsImpl {
NON_COPYABLE(ThreadManagerWindowsImpl);
NON_MOVEABLE(ThreadManagerWindowsImpl);
private:
DWORD m_tls_index;
public:
explicit ThreadManagerWindowsImpl(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);
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
void NotifyThreadNameChangedImpl(const ThreadType *thread) const;
void SetCurrentThread(ThreadType *thread) const;
ThreadType *GetCurrentThread() const;
s32 GetCurrentCoreNumber() const;
s32 GetDefaultCoreNumber() const { return 0; }
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;
bool MapAliasStack(void **out, void *stack, size_t size) {
AMS_UNUSED(stack, size);
*out = nullptr;
return true;
}
bool UnmapAliasStack(void *alias_stack, void *original_stack, size_t size) {
AMS_UNUSED(alias_stack, original_stack, size);
return true;
}
NORETURN void ExitProcessImpl() {
AMS_ABORT("TODO: Just exit?");
}
NORETURN void QuickExit() {
AMS_ABORT("TODO: Just exit?");
}
};
using ThreadManagerImpl = ThreadManagerWindowsImpl;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) 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>
#if defined(AMS_OS_IMPL_USE_PTHREADS)
#include "os_thread_manager_impl.pthread.inc"
#endif

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 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 ThreadManagerPthreadImpl {
NON_COPYABLE(ThreadManagerPthreadImpl);
NON_MOVEABLE(ThreadManagerPthreadImpl);
private:
pthread_key_t m_tls_key;
public:
explicit ThreadManagerPthreadImpl(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);
/* TODO: void GetThreadContextUnsafe(ThreadContextInfo *out_context, const ThreadType *thread); */
void NotifyThreadNameChangedImpl(const ThreadType *thread) const;
void SetCurrentThread(ThreadType *thread) const;
ThreadType *GetCurrentThread() const;
s32 GetCurrentCoreNumber() const;
s32 GetDefaultCoreNumber() const { return 0; }
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;
bool MapAliasStack(void **out, void *stack, size_t size) {
AMS_UNUSED(stack, size);
*out = nullptr;
return true;
}
bool UnmapAliasStack(void *alias_stack, void *original_stack, size_t size) {
AMS_UNUSED(alias_stack, original_stack, size);
return true;
}
NORETURN void ExitProcessImpl() {
AMS_ABORT("TODO: Just exit?");
}
NORETURN void QuickExit() {
AMS_ABORT("TODO: Just exit?");
}
};
using ThreadManagerImpl = ThreadManagerPthreadImpl;
}

View File

@@ -0,0 +1,367 @@
/*
* Copyright (c) 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.pthread.hpp"
#include "os_thread_manager.hpp"
#include <pthread.h>
#if defined(ATMOSPHERE_OS_LINUX)
#include <sched.h>
#elif defined(ATMOSPHERE_OS_MACOS)
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
namespace {
struct cpu_set_t {
uint64_t affinity_mask;
};
ALWAYS_INLINE void CPU_ZERO(cpu_set_t *cs) { cs->affinity_mask = 0; }
ALWAYS_INLINE void CPU_SET(int core, cpu_set_t *cs) { cs->affinity_mask |= (UINT64_C(1) << core); }
ALWAYS_INLINE bool CPU_ISSET(int core, cpu_set_t *cs) { return cs->affinity_mask & (UINT64_C(1) << core); }
constexpr size_t CPU_SETSIZE = BITSIZEOF(cpu_set_t{}.affinity_mask);
int sched_getaffinity(pid_t pid, size_t cpu_size, cpu_set_t *cs) {
/* Ignore the process id/cpu size arguments. */
static_cast<void>(pid);
static_cast<void>(cpu_size);
/* Get the core count. */
int32_t core_count = 0;
size_t size = sizeof(core_count);
if (const auto ret = ::sysctlbyname("machdep.cpu.core_count", std::addressof(core_count), std::addressof(size), 0, 0); ret != 0) {
return ret;
}
/* Set our cpu set structure. */
cs->affinity_mask = 0;
for (auto i = 0; i < core_count; ++i) {
cs->affinity_mask |= (UINT64_C(1) << i);
}
return 0;
}
int pthread_setaffinity_np(pthread_t thread, size_t cpu_size, cpu_set_t *cs) {
/* Ignore the cpu size argument. */
static_cast<void>(cpu_size);
/* If the thread is allowed to be on more than one core, we'll ignore it. */
/* TODO: Do this properly? */
if (const auto pc = std::popcount(cs->affinity_mask); pc == 0 || pc > 1) {
return 0;
}
/* Create policy to bind to the core. */
thread_affinity_policy_data_t policy = { std::countr_zero(cs->affinity_mask) };
/* Get the underlying mach thread. */
thread_port_t mach_thread = pthread_mach_thread_np(thread);
/* Set the policy. */
thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, reinterpret_cast<thread_policy_t>(std::addressof(policy)), 1);
return 0;
}
}
#else
#error "Unknown OS for pthread CoreId get"
#endif
namespace ams::os::impl {
namespace {
constexpr size_t DefaultStackSize = 1_MB;
void *InvokeThread(void *arg) {
ThreadType *thread = static_cast<ThreadType *>(arg);
/* Invoke the thread. */
ThreadManager::InvokeThread(thread);
/* Set exit state. */
{
std::scoped_lock lk(util::GetReference(thread->cs_pthread_exit));
AMS_ASSERT(thread->exited_pthread == false);
thread->exited_pthread = true;
util::GetReference(thread->cv_pthread_exit).Broadcast();
}
return nullptr;
}
os::ThreadType *DynamicAllocateAndRegisterThreadType() {
/* Get the thread manager. */
auto &thread_manager = GetThreadManager();
/* Allocate a thread. */
auto *thread = thread_manager.AllocateThreadType();
AMS_ABORT_UNLESS(thread != nullptr);
/* Setup the thread object. */
SetupThreadObjectUnsafe(thread, nullptr, nullptr, nullptr, nullptr, 0, DefaultThreadPriority);
thread->state = ThreadType::State_Started;
thread->auto_registered = true;
/* Set the thread's pthread handle. */
thread->pthread = pthread_self();
thread->ideal_core = thread_manager.GetCurrentCoreNumber();
thread->affinity_mask = thread_manager.GetThreadAvailableCoreMask();
/* Place the object under the thread manager. */
thread_manager.PlaceThreadObjectUnderThreadManagerSafe(thread);
return thread;
}
}
ThreadManagerPthreadImpl::ThreadManagerPthreadImpl(ThreadType *main_thread) {
/* Create tls slot for thread pointer. */
AMS_ABORT_UNLESS(pthread_key_create(std::addressof(m_tls_key), nullptr) == 0);
/* Setup the main thread object. */
SetupThreadObjectUnsafe(main_thread, nullptr, nullptr, nullptr, nullptr, DefaultStackSize, DefaultThreadPriority);
/* Setup the main thread's pthread information. */
main_thread->pthread = pthread_self();
main_thread->ideal_core = this->GetCurrentCoreNumber();
main_thread->affinity_mask = this->GetThreadAvailableCoreMask();
}
Result ThreadManagerPthreadImpl::CreateThread(ThreadType *thread, s32 ideal_core) {
/* Create the assert. */
/* TODO: Check for failure properly. */
pthread_t pthread;
const auto res = pthread_create(std::addressof(pthread), nullptr, &InvokeThread, thread);
AMS_ASSERT(res == 0);
/* Set the thread's pthread handle information. */
thread->pthread = pthread;
thread->ideal_core = ideal_core;
thread->affinity_mask = this->GetThreadAvailableCoreMask();
R_SUCCEED();
}
void ThreadManagerPthreadImpl::DestroyThreadUnsafe(ThreadType *thread) {
/* The thread must have exited. */
{
std::scoped_lock lk(util::GetReference(thread->cs_pthread_exit));
AMS_ABORT_UNLESS(thread->exited_pthread);
}
/* Join the thread. */
const auto ret = pthread_join(thread->pthread, nullptr);
AMS_ASSERT(ret == 0);
AMS_UNUSED(ret);
}
void ThreadManagerPthreadImpl::StartThread(const ThreadType *thread) {
/* Nothing is actually needed here, because pthreads cannot start suspended. */
/* TODO: Should we add a condvar/mutex for thread start? */
AMS_UNUSED(thread);
}
void ThreadManagerPthreadImpl::WaitForThreadExit(ThreadType *thread) {
/* Wait for the thread to exit. */
{
std::scoped_lock lk(util::GetReference(thread->cs_pthread_exit));
while (!thread->exited_pthread) {
util::GetReference(thread->cv_pthread_exit).Wait(util::GetPointer(thread->cs_pthread_exit));
}
}
}
bool ThreadManagerPthreadImpl::TryWaitForThreadExit(ThreadType *thread) {
/* Check if the thread has exited. */
std::scoped_lock lk(util::GetReference(thread->cs_pthread_exit));
return thread->exited_pthread;
}
void ThreadManagerPthreadImpl::YieldThread() {
#if defined(ATMOSPHERE_OS_MACOS)
sched_yield();
#else
const auto ret = pthread_yield();
AMS_ASSERT(ret == 0);
AMS_UNUSED(ret);
#endif
}
bool ThreadManagerPthreadImpl::ChangePriority(ThreadType *thread, s32 priority) {
/* TODO: Should we set the thread's niceness value? */
AMS_UNUSED(thread, priority);
return true;
}
s32 ThreadManagerPthreadImpl::GetCurrentPriority(const ThreadType *thread) const {
return thread->base_priority;
}
ThreadId ThreadManagerPthreadImpl::GetThreadId(const ThreadType *thread) const {
#if defined(AMS_OS_IMPL_USE_PTHREADID_NP_FOR_THREAD_ID)
ThreadId tid;
const auto ret = pthread_threadid_np(thread->pthread, std::addressof(tid));
AMS_ABORT_UNLESS(ret == 0);
return tid;
#else
return thread->pthread;
#endif
}
void ThreadManagerPthreadImpl::SuspendThreadUnsafe(ThreadType *thread) {
AMS_UNUSED(thread);
AMS_ABORT("TODO: Linux SuspendThread Signal/Pause impl?");
}
void ThreadManagerPthreadImpl::ResumeThreadUnsafe(ThreadType *thread) {
AMS_UNUSED(thread);
AMS_ABORT("TODO: ResumeThread Signal/Pause impl?");
}
void ThreadManagerPthreadImpl::NotifyThreadNameChangedImpl(const ThreadType *thread) const {
/* TODO */
AMS_UNUSED(thread);
}
void ThreadManagerPthreadImpl::SetCurrentThread(ThreadType *thread) const {
const auto ret = pthread_setspecific(m_tls_key, thread);
AMS_ASSERT(ret == 0);
AMS_UNUSED(ret);
}
ThreadType *ThreadManagerPthreadImpl::GetCurrentThread() const {
/* Get the thread from tls index. */
ThreadType *thread = static_cast<ThreadType *>(pthread_getspecific(m_tls_key));
/* If the thread's TLS isn't set, we need to find it (and set tls) or make it. */
if (thread == nullptr) {
/* Get the current thread id. */
ThreadId self_tid;
pthread_t self_thread = pthread_self();
#if defined(AMS_OS_IMPL_USE_PTHREADID_NP_FOR_THREAD_ID)
const auto ret = pthread_threadid_np(self_thread, std::addressof(self_tid));
AMS_ABORT_UNLESS(ret == 0);
#else
self_tid = self_thread;
#endif
/* Try to find the thread. */
thread = GetThreadManager().FindThreadTypeById(self_tid);
if (thread == nullptr) {
/* Create the thread. */
thread = DynamicAllocateAndRegisterThreadType();
}
/* Set the thread's TLS. */
this->SetCurrentThread(thread);
}
return thread;
}
s32 ThreadManagerPthreadImpl::GetCurrentCoreNumber() const {
#if defined(ATMOSPHERE_OS_LINUX)
const auto core = sched_getcpu();
AMS_ABORT_UNLESS(core >= 0);
return core;
#elif defined(ATMOSPHERE_OS_MACOS)
return 0;
#else
AMS_ABORT("TODO: Unknown OS GetCurrentCoreNumber() under pthreads");
#endif
}
void ThreadManagerPthreadImpl::SetThreadCoreMask(ThreadType *thread, s32 ideal_core, u64 affinity_mask) const {
/* If we should use the default, set the actual ideal core. */
if (ideal_core == IdealCoreUseDefault) {
affinity_mask = this->GetThreadAvailableCoreMask();
ideal_core = util::CountTrailingZeros(affinity_mask);
affinity_mask = static_cast<decltype(affinity_mask)>(1) << ideal_core;
}
/* Lock the thread. */
std::scoped_lock lk(util::GetReference(thread->cs_thread));
/* Build the cpu affinity. */
cpu_set_t cpuset;
CPU_ZERO(std::addressof(cpuset));
for (size_t i = 0; i < std::min<size_t>(BITSIZEOF(affinity_mask), CPU_SETSIZE); ++i) {
if ((static_cast<decltype(affinity_mask)>(1) << i) & affinity_mask) {
CPU_SET(i, std::addressof(cpuset));
}
}
/* Set the cpu affinity. */
const auto ret = pthread_setaffinity_np(thread->pthread, sizeof(cpuset), std::addressof(cpuset));
AMS_ABORT_UNLESS(ret == 0);
/* Set the ideal core. */
if (ideal_core != IdealCoreNoUpdate) {
thread->ideal_core = ideal_core;
}
/* Set the tracked affinity mask. */
thread->affinity_mask = affinity_mask;
}
void ThreadManagerPthreadImpl::GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const {
/* Lock the thread. */
std::scoped_lock lk(util::GetReference(thread->cs_thread));
/* Set the output. */
if (out_ideal_core != nullptr) {
*out_ideal_core = thread->ideal_core;
}
if (out_affinity_mask != nullptr) {
*out_affinity_mask = thread->affinity_mask;
}
}
u64 ThreadManagerPthreadImpl::GetThreadAvailableCoreMask() const {
#if defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
cpu_set_t cpuset;
CPU_ZERO(std::addressof(cpuset));
const auto ret = sched_getaffinity(0, sizeof(cpuset), std::addressof(cpuset));
AMS_ASSERT(ret == 0);
AMS_UNUSED(ret);
u64 mask = 0;
for (size_t i = 0; i < std::min<size_t>(BITSIZEOF(mask), CPU_SETSIZE); ++i) {
if (CPU_ISSET(i, std::addressof(cpuset))) {
mask |= static_cast<decltype(mask)>(1) << i;
}
}
AMS_ASSERT(mask != 0);
return mask;
#else
AMS_ABORT("TODO: Unknown OS GetThreadAvailableCoreMask() under pthreads");
#endif
}
}

View File

@@ -16,8 +16,12 @@
#pragma once
#include <stratosphere.hpp>
#ifdef ATMOSPHERE_OS_HORIZON
#if defined(AMS_OS_IMPL_USE_PTHREADS)
#include "os_thread_manager_impl.pthread.hpp"
#elif defined(ATMOSPHERE_OS_HORIZON)
#include "os_thread_manager_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_thread_manager_impl.os.windows.hpp"
#else
#error "Unknown OS for ThreadManagerImpl"
#endif
@@ -65,12 +69,14 @@ namespace ams::os::impl {
ThreadManager();
void CleanupThread();
void CleanupThread(ThreadType *thread);
s32 GetThreadCountForDebug() const { return m_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 DestroyThreadObject(ThreadType *thread);
void StartThread(ThreadType *thread);
void WaitThread(ThreadType *thread);
bool TryWaitThread(ThreadType *thread);
@@ -84,8 +90,8 @@ namespace ams::os::impl {
s32 SuspendThread(ThreadType *thread);
s32 ResumeThread(ThreadType *thread);
void CancelThreadSynchronization(ThreadType *thread);
void ExitProcess() { return m_impl.ExitProcessImpl(); }
void QuickExit() { return m_impl.QuickExit(); }
/* TODO void GetThreadContext(ThreadContextInfo *out_context, const ThreadType *thread); */
void SetInitialThreadNameUnsafe(ThreadType *thread);
@@ -97,6 +103,8 @@ namespace ams::os::impl {
void GetThreadCoreMask(s32 *out_ideal_core, u64 *out_affinity_mask, const ThreadType *thread) const { return m_impl.GetThreadCoreMask(out_ideal_core, out_affinity_mask, thread); }
u64 GetThreadAvailableCoreMask() const { return m_impl.GetThreadAvailableCoreMask(); }
void SetZeroToAllThreadsTlsSafe(int slot);
void PushBackToAllThreadsListUnsafe(ThreadType *thread) {
m_all_threads_list.push_back(*thread);
++m_num_created_threads;
@@ -128,11 +136,11 @@ namespace ams::os::impl {
}
ThreadType *AllocateThreadType() const {
return reinterpret_cast<ThreadType *>(std::malloc(sizeof(ThreadType)));
return static_cast<ThreadType *>(ams::Malloc(sizeof(ThreadType)));
}
void FreeThreadType(ThreadType *thread) const {
std::free(thread);
ams::Free(thread);
}
const ThreadType *GetMainThread() const {
@@ -146,6 +154,22 @@ namespace ams::os::impl {
ThreadId GetThreadId(const ThreadType *thread) {
return m_impl.GetThreadId(thread);
}
ThreadType *FindThreadTypeById(ThreadId id) {
std::scoped_lock lk(m_cs);
for (auto rit = m_all_threads_list.rbegin(); rit != m_all_threads_list.rend(); ++rit) {
auto * const thread = std::addressof(*rit);
if (this->GetThreadId(thread) == id) {
return thread;
}
}
return nullptr;
}
private:
bool CreateAliasStackUnsafe(ThreadType *thread);
void DeleteAliasStackUnsafe(ThreadType *thread);
public:
static void InvokeThread(ThreadType *thread);
};

View File

@@ -16,8 +16,14 @@
#pragma once
#include <stratosphere.hpp>
#ifdef ATMOSPHERE_OS_HORIZON
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_tick_manager_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_tick_manager_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_tick_manager_impl.std_chrono.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_tick_manager_impl.std_chrono.hpp"
#else
#error "Unknown OS for TickManagerImpl"
#endif
@@ -31,7 +37,11 @@ namespace ams::os::impl {
private:
TickManagerImpl m_impl;
public:
#if defined(ATMOSPHERE_OS_HORIZON)
constexpr TickManager() : m_impl() { /* ... */ }
#else
TickManager() : m_impl() { /* ... */ }
#endif
ALWAYS_INLINE Tick GetTick() const {
return m_impl.GetTick();

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 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 <mmsystem.h>
namespace ams::os::impl {
class TickManagerImpl {
private:
s64 m_tick_frequency;
TimeSpan m_max_time;
s64 m_max_tick;
public:
TickManagerImpl() {
/* Get the tick frequency. */
::timeBeginPeriod(1);
LARGE_INTEGER freq;
::QueryPerformanceFrequency(std::addressof(freq));
m_tick_frequency = static_cast<s64>(freq.QuadPart);
/* Set maximums. */
constexpr s64 TickFrequencyForNanoSecondResolution = TimeSpan::FromSeconds(1).GetNanoSeconds();
if (m_tick_frequency <= TickFrequencyForNanoSecondResolution) {
m_max_tick = m_tick_frequency * (std::numeric_limits<s64>::max() / TickFrequencyForNanoSecondResolution);
m_max_time = TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max());
} else {
m_max_tick = std::numeric_limits<s64>::max();
m_max_time = TimeSpan::FromSeconds(std::numeric_limits<s64>::max() / m_tick_frequency);
}
}
~TickManagerImpl() {
::timeEndPeriod(1);
}
ALWAYS_INLINE Tick GetTick() const {
LARGE_INTEGER freq;
::QueryPerformanceFrequency(std::addressof(freq));
return Tick(static_cast<s64>(freq.QuadPart));
}
ALWAYS_INLINE Tick GetSystemTickOrdered() const {
LARGE_INTEGER freq;
PerformOrderingForGetSystemTickOrdered();
::QueryPerformanceFrequency(std::addressof(freq));
PerformOrderingForGetSystemTickOrdered();
return Tick(static_cast<s64>(freq.QuadPart));
}
ALWAYS_INLINE s64 GetTickFrequency() const {
return m_tick_frequency;
}
ALWAYS_INLINE s64 GetMaxTick() const {
return m_max_tick;
}
ALWAYS_INLINE s64 GetMaxTimeSpanNs() const {
return m_max_time.GetNanoSeconds();
}
private:
static ALWAYS_INLINE void PerformOrderingForGetSystemTickOrdered() {
int a = 0, b, c = 0, d;
__asm__ __volatile__("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(a), "2"(c) : "memory");
}
};
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 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 <chrono>
namespace ams::os::impl {
class TickManagerImpl {
private:
using StandardClock = typename std::conditional<std::chrono::high_resolution_clock::is_steady, std::chrono::high_resolution_clock, std::chrono::steady_clock>::type;
using TimePoint = std::chrono::time_point<StandardClock>;
private:
TimePoint m_start_time;
public:
TickManagerImpl() : m_start_time(StandardClock::now()) { /* ... */ }
ALWAYS_INLINE Tick GetTick() const {
return Tick(static_cast<s64>((StandardClock::now() - m_start_time).count()));
}
ALWAYS_INLINE Tick GetSystemTickOrdered() const {
PerformOrderingForGetSystemTickOrdered(true);
ON_SCOPE_EXIT { PerformOrderingForGetSystemTickOrdered(false); };
return Tick(static_cast<s64>((StandardClock::now() - m_start_time).count()));
}
static constexpr ALWAYS_INLINE s64 GetTickFrequency() {
return static_cast<s64>(StandardClock::period::den) / static_cast<s64>(StandardClock::period::num);
}
static constexpr ALWAYS_INLINE s64 GetMaxTick() {
static_assert(GetTickFrequency() <= TimeSpan::FromSeconds(1).GetNanoSeconds());
return (std::numeric_limits<s64>::max() / TimeSpan::FromSeconds(1).GetNanoSeconds()) * GetTickFrequency();
}
static constexpr ALWAYS_INLINE s64 GetMaxTimeSpanNs() {
static_assert(GetTickFrequency() <= TimeSpan::FromSeconds(1).GetNanoSeconds());
return TimeSpan::FromNanoSeconds(std::numeric_limits<s64>::max()).GetNanoSeconds();
}
private:
static ALWAYS_INLINE void PerformOrderingForGetSystemTickOrdered(bool before) {
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
int a = 0, b, c = 0, d;
__asm__ __volatile__("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(a), "2"(c) : "memory");
AMS_UNUSED(before);
#elif defined(ATMOSPHERE_ARCH_ARM64)
if (before) {
__asm__ __volatile__("dsb ish" ::: "memory");
}
__asm__ __volatile__("isb" ::: "memory");
#else
#error "Unknown architecture for std::chrono TickManager ordering."
#endif
}
};
}

View File

@@ -19,6 +19,12 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_timeout_helper_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_timeout_helper_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_timeout_helper_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_timeout_helper_impl.os.macos.hpp"
#else
#error "Unknown OS for ams::os::TimeoutHelper"
#endif

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 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.linux.hpp"
#include <time.h>
namespace ams::os::impl {
void TimeoutHelperImpl::Sleep(TimeSpan tm) {
/* If asked to sleep for no time, do nothing */
if (tm == 0) {
return;
}
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
const s64 ns = tm.GetNanoSeconds();
struct timespec ts = { .tv_sec = (ns / NanoSecondsPerSecond), .tv_nsec = ns % NanoSecondsPerSecond };
while (true) {
const auto ret = ::nanosleep(std::addressof(ts), std::addressof(ts));
if (ret == 0) {
break;
}
AMS_ASSERT(errno == EINTR);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_tick_manager.hpp"
namespace ams::os::impl {
using TargetTimeSpan = ::ams::TimeSpan;
class TimeoutHelperImpl {
public:
static TargetTimeSpan ConvertToImplTime(Tick tick) {
return impl::GetTickManager().ConvertToTimeSpan(tick);
}
static void Sleep(TimeSpan tm);
};
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 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.macos.hpp"
#include <time.h>
namespace ams::os::impl {
void TimeoutHelperImpl::Sleep(TimeSpan tm) {
/* If asked to sleep for no time, do nothing */
if (tm == 0) {
return;
}
constexpr s64 NanoSecondsPerSecond = TimeSpan::FromSeconds(1).GetNanoSeconds();
const s64 ns = tm.GetNanoSeconds();
struct timespec ts = { .tv_sec = (ns / NanoSecondsPerSecond), .tv_nsec = ns % NanoSecondsPerSecond };
while (true) {
const auto ret = ::nanosleep(std::addressof(ts), std::addressof(ts));
if (ret == 0) {
break;
}
AMS_ASSERT(errno == EINTR);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "os_tick_manager.hpp"
namespace ams::os::impl {
using TargetTimeSpan = ::ams::TimeSpan;
class TimeoutHelperImpl {
public:
static TargetTimeSpan ConvertToImplTime(Tick tick) {
return impl::GetTickManager().ConvertToTimeSpan(tick);
}
static void Sleep(TimeSpan tm);
};
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 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.windows.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
void TimeoutHelperImpl::Sleep(TimeSpan tm) {
/* If asked to sleep for no time, do nothing */
if (tm == 0) {
return;
}
/* Get the end tick. */
auto &tick_manager = GetTickManager();
u64 tick_current = tick_manager.GetTick().GetInt64Value();
u64 tick_timeout = tick_manager.ConvertToTick(tm).GetInt64Value();
u64 tick_end = tick_current + tick_timeout + 1;
const auto end = os::Tick(std::min<u64>(std::numeric_limits<s64>::max(), tick_end));
while (true) {
const auto tick = tick_manager.GetTick();
if (tick >= end) {
return;
}
::Sleep(ConvertToImplTime(end - tick));
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 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 = DWORD;
class TimeoutHelperImpl {
public:
static TargetTimeSpan ConvertToImplTime(Tick tick) {
constexpr s64 MaxTime = std::numeric_limits<s64>::max() - TimeSpan::FromMilliSeconds(1).GetNanoSeconds();
constexpr auto Ratio = TimeSpan::FromMilliSeconds(1);
constexpr auto NanoS = TimeSpan::FromNanoSeconds(1);
const auto time = TimeSpan::FromNanoSeconds(std::min<s64>(MaxTime, impl::GetTickManager().ConvertToTimeSpan(tick).GetNanoSeconds()));
return static_cast<TargetTimeSpan>((time + Ratio - NanoS).GetMilliSeconds());
}
static void Sleep(TimeSpan tm);
};
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 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_tls_manager.hpp"
#include "os_thread_manager.hpp"
namespace ams::os::impl {
/* TODO: Should we migrate away from libnx to this on NX at some point? */
#if !defined(ATMOSPHERE_OS_HORIZON)
namespace {
constexpr auto TryCallDestructorCount = 4;
void DefaultTlsDestructor(uintptr_t) {
/* ... */
}
}
void TlsManager::InvokeTlsDestructors() {
/* Get the curent thread. */
auto * const thread = impl::GetCurrentThread();
/* Call all destructors. */
for (int slot = 0; slot < static_cast<int>(TotalTlsSlotCountMax); ++slot) {
if (const auto destructor = m_destructors[slot]; destructor != nullptr) {
destructor(thread->tls_value_array[slot]);
}
thread->tls_value_array[slot] = 0;
}
/* Verify all tls values are wiped, trying up to four times. */
for (auto i = 0; i < TryCallDestructorCount; ++i) {
bool all_cleared = true;
for (auto slot = 0; slot < static_cast<int>(TotalTlsSlotCountMax); ++slot) {
if (thread->tls_value_array[slot] != 0) {
all_cleared = false;
if (const auto destructor = m_destructors[slot]; destructor != nullptr) {
destructor(thread->tls_value_array[slot]);
}
thread->tls_value_array[slot] = 0;
}
}
if (all_cleared) {
break;
}
}
}
bool TlsManager::AllocateTlsSlot(TlsSlot *out, TlsDestructor destructor, bool sdk) {
/* Decide on a slot. */
int slot;
{
/* Lock appropriately. */
std::scoped_lock lk(util::GetReference(impl::GetCurrentThread()->cs_thread));
std::scoped_lock lk2(m_cs);
/* If we're out of tls slots, fail. */
if (!sdk && m_used >= static_cast<int>(TlsSlotCountMax)) {
return false;
}
/* Get a slot. */
slot = SearchUnusedTlsSlotUnsafe(sdk);
/* Set the destructor. */
m_destructors[slot] = (destructor != nullptr) ? destructor : DefaultTlsDestructor;
/* Increment our used count, if the slot was a user slot. */
if (!sdk) {
++m_used;
}
}
/* Zero the slot in all threads. */
GetThreadManager().SetZeroToAllThreadsTlsSafe(slot);
/* Set the output slot's value. */
out->_value = slot;
return true;
}
void TlsManager::FreeTlsSlot(TlsSlot slot) {
/* Get the slot's index. */
const auto slot_idx = slot._value;
/* Lock appropriately. */
std::scoped_lock lk(util::GetReference(impl::GetCurrentThread()->cs_thread));
std::scoped_lock lk2(m_cs);
/* Verify the slot is allocated. */
AMS_ABORT_UNLESS(m_destructors[slot_idx] != nullptr);
/* Free the slot. */
m_destructors[slot_idx] = nullptr;
/* Decrement our used count, if the slot was a user slot. */
if (slot_idx < TlsSlotCountMax) {
--m_used;
}
}
int TlsManager::SearchUnusedTlsSlotUnsafe(bool sdk) {
if (sdk) {
/* Search backwards for an unused slot. */
for (int slot = static_cast<int>(TotalTlsSlotCountMax) - 1; slot >= static_cast<int>(TlsSlotCountMax + SdkInternalTlsCount); --slot) {
if (m_destructors[slot] == nullptr) {
return slot;
}
}
AMS_ABORT("Failed to allocate SdkTlsSlot");
} else {
/* Search forwards for an unused slot. */
for (int slot = 0; slot < static_cast<int>(TlsSlotCountMax); ++slot) {
if (m_destructors[slot] == nullptr) {
return slot;
}
}
AMS_ABORT("Failed to allocate TlsSlot");
}
}
#endif
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 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_resource_manager.hpp"
namespace ams::os::impl {
ALWAYS_INLINE TlsManager &GetTlsManager() {
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(TlsManager, s_tls_manager);
return s_tls_manager;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 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 {
static constexpr size_t TotalTlsSlotCountMax = TlsSlotCountMax + SdkTlsSlotCountMax;
class TlsManager {
NON_COPYABLE(TlsManager);
NON_MOVEABLE(TlsManager);
private:
TlsDestructor m_destructors[TotalTlsSlotCountMax];
InternalCriticalSection m_cs;
int m_used;
public:
consteval TlsManager() : m_destructors(), m_cs(), m_used() { /* ... */ }
void InvokeTlsDestructors();
bool AllocateTlsSlot(TlsSlot *out, TlsDestructor destructor, bool sdk);
void FreeTlsSlot(TlsSlot slot);
int GetUsedTlsSlots() const { return m_used; }
static consteval int GetMaxTlsSlots() { return TotalTlsSlotCountMax; }
private:
int SearchUnusedTlsSlotUnsafe(bool sdk);
};
}

View File

@@ -0,0 +1,397 @@
/*
* Copyright (c) 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_vamm_manager.hpp"
#if defined(ATMOSPHERE_OS_HORIZON)
#include "os_vamm_manager_impl.os.horizon.hpp"
#elif defined(ATMOSPHERE_OS_WINDOWS)
#include "os_vamm_manager_impl.os.windows.hpp"
#elif defined(ATMOSPHERE_OS_LINUX)
#include "os_vamm_manager_impl.os.linux.hpp"
#elif defined(ATMOSPHERE_OS_MACOS)
#include "os_vamm_manager_impl.os.macos.hpp"
#else
#error "Unknown OS for VammManagerImpl"
#endif
namespace ams::os::impl {
namespace {
enum AddressAllocationResult {
AddressAllocationResult_Success,
AddressAllocationResult_OutOfMemory,
AddressAllocationResult_OutOfSpace,
};
class AddressRegion : public util::IntrusiveRedBlackTreeBaseNode<AddressRegion> {
private:
uintptr_t m_address;
size_t m_size;
public:
ALWAYS_INLINE AddressRegion(uintptr_t a, size_t s) : m_address(a), m_size(s) { /* ... */ }
constexpr ALWAYS_INLINE uintptr_t GetAddressBegin() const { return m_address; }
constexpr ALWAYS_INLINE uintptr_t GetAddressEnd() const { return m_address + m_size; }
constexpr ALWAYS_INLINE size_t GetSize() const { return m_size; }
constexpr ALWAYS_INLINE bool IsContained(uintptr_t address) const {
if (address < m_address) {
return false;
} else if (address < this->GetAddressEnd()) {
return true;
} else {
return false;
}
}
constexpr ALWAYS_INLINE bool IsContained(uintptr_t address, size_t size) const {
const uintptr_t end = address + size;
if (!(address <= end)) {
return false;
}
if (!(this->GetAddressBegin() <= address)) {
return false;
}
if (!(end <= this->GetAddressEnd())) {
return false;
}
return true;
}
};
struct AddressRegionCompare {
using RedBlackKeyType = uintptr_t;
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType a, const RedBlackKeyType &b) {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
}
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &a, const AddressRegion &b) {
return Compare(a, b.GetAddressBegin());
}
static constexpr ALWAYS_INLINE int Compare(const AddressRegion &a, const AddressRegion &b) {
return Compare(a.GetAddressBegin(), b.GetAddressBegin());
}
};
using AddressRegionTree = util::IntrusiveRedBlackTreeBaseTraits<AddressRegion>::TreeType<AddressRegionCompare>;
class DynamicUnitHeap {
private:
static constexpr size_t PhysicalMemoryUnitSize = MemoryPageSize;
private:
uintptr_t m_start;
uintptr_t m_limit;
uintptr_t m_end;
util::TypedStorage<lmem::HeapCommonHead> m_head;
lmem::HeapHandle m_heap;
public:
DynamicUnitHeap(uintptr_t address, size_t size, size_t unit_size) : m_start(address), m_end(address + size) {
/* Allocate the start of our buffer. */
VammManagerImpl::AllocatePhysicalMemoryImpl(m_start, PhysicalMemoryUnitSize);
/* Set our current limit. */
m_limit = m_start + PhysicalMemoryUnitSize;
/* Initialize our heap. */
m_heap = lmem::CreateUnitHeap(reinterpret_cast<void *>(m_start), PhysicalMemoryUnitSize, unit_size, lmem::CreateOption_None, alignof(u64), util::GetPointer(m_head));
}
void *Allocate() {
void *alloc = lmem::AllocateFromUnitHeap(m_heap);
if (alloc == nullptr) {
this->Extend();
alloc = lmem::AllocateFromUnitHeap(m_heap);
}
return alloc;
}
void Free(void *p) {
lmem::FreeToUnitHeap(m_heap, p);
}
private:
void Extend() {
AMS_ABORT_UNLESS(m_limit < m_end);
if (R_SUCCEEDED(VammManagerImpl::AllocatePhysicalMemoryImpl(m_limit, PhysicalMemoryUnitSize))) {
m_limit += PhysicalMemoryUnitSize;
lmem::ExtendUnitHeap(m_heap, PhysicalMemoryUnitSize);
}
}
};
}
class AddressRegionManager {
private:
static constexpr size_t UnitHeapRegionSize = 1_GB - 2_MB;
private:
uintptr_t m_start;
size_t m_size;
AddressRegionTree m_tree;
DynamicUnitHeap m_heap;
public:
AddressRegionManager(uintptr_t start, size_t size) : m_start(start), m_size(size), m_heap(start, UnitHeapRegionSize, sizeof(AddressRegion)) {
/* Insert a block in the tree for our heap. */
m_tree.insert(*(new (m_heap.Allocate()) AddressRegion(m_start, UnitHeapRegionSize)));
/* Insert a zero-size block in the tree at the end of our heap. */
m_tree.insert(*(new (m_heap.Allocate()) AddressRegion(m_start + size, 0)));
}
AddressAllocationResult Allocate(AddressRegion **out, size_t size) {
/* Allocate a region. */
void *p = m_heap.Allocate();
if (p == nullptr) {
return AddressAllocationResult_OutOfMemory;
}
/* Determine alignment for the specified size. */
const size_t align = SelectAlignment(size);
/* Iterate, looking for an appropriate region. */
auto *region = std::addressof(m_tree.back());
while (true) {
/* Get the previous region. */
auto *prev = region->GetPrev();
if (prev == nullptr) {
break;
}
/* Get the space between prev and the current region. */
const uintptr_t space_start = prev->GetAddressEnd() + MemoryPageSize;
const uintptr_t space_end = region->GetAddressBegin() - MemoryPageSize;
const size_t space_size = space_end - space_start;
/* If there's enough space in the region, consider it further. */
if (space_size >= size) {
/* Determine the allocation region extents. */
const uintptr_t alloc_start = util::AlignUp(space_start, align);
const uintptr_t alloc_end = alloc_start + size;
/* If the allocation works, use it. */
if (alloc_end <= space_end) {
auto *address_region = new (p) AddressRegion(alloc_start, size);
m_tree.insert(*address_region);
*out = address_region;
return AddressAllocationResult_Success;
}
}
/* Otherwise, continue. */
region = prev;
}
/* We ran out of space to allocate. */
return AddressAllocationResult_OutOfSpace;
}
void Free(AddressRegion *region) {
m_tree.erase(m_tree.iterator_to(*region));
m_heap.Free(region);
}
bool IsAlreadyAllocated(uintptr_t address, size_t size) const {
/* Find the first region >= our address. */
auto region = std::addressof(*(m_tree.nfind_key(address)));
if (region == nullptr) {
return false;
}
/* If the address matches, return whether the region is contained. */
if (region->GetAddressBegin() == address) {
return size <= region->GetSize();
}
/* Otherwise, check the previous entry. */
if (region = region->GetPrev(); region == nullptr) {
return false;
}
return region->IsContained(address, size);
}
AddressRegion *Find(uintptr_t address) const {
return std::addressof(*(m_tree.find_key(address)));
}
private:
static constexpr size_t SelectAlignment(size_t size) {
if (size < 4_MB) {
if (size < 2_MB) {
return 64_KB;
} else {
return 2_MB;
}
} else {
if (size < 32_MB) {
return 4_MB;
} else if (size < 1_GB) {
return 32_MB;
} else {
return 1_GB;
}
}
}
};
namespace {
constinit util::TypedStorage<AddressRegionManager> g_address_region_manager_storage = {};
}
VammManager::VammManager() : m_lock(), m_region_manager(nullptr) {
/* Get the reserved region. */
VammManagerImpl::GetReservedRegionImpl(std::addressof(m_reserved_region_start), std::addressof(m_reserved_region_size));
}
void VammManager::InitializeIfEnabled() {
/* Acquire exclusive/writer access. */
std::scoped_lock lk(m_lock);
/* Initialize, if we haven't already. */
if (m_region_manager == nullptr && IsVirtualAddressMemoryEnabled()) {
m_region_manager = util::ConstructAt(g_address_region_manager_storage, m_reserved_region_start, m_reserved_region_size);
}
}
Result VammManager::AllocateAddressRegion(uintptr_t *out, size_t size) {
/* Allocate an address. */
uintptr_t address;
{
/* Lock access to our region manager. */
std::scoped_lock lk(m_lock);
AMS_ASSERT(m_region_manager != nullptr);
/* Allocate an address region. */
AddressRegion *region;
switch (m_region_manager->Allocate(std::addressof(region), size)) {
case AddressAllocationResult_Success:
address = region->GetAddressBegin();
break;
case AddressAllocationResult_OutOfSpace:
R_THROW(os::ResultOutOfVirtualAddressSpace());
default:
R_THROW(os::ResultOutOfMemory());
}
}
/* Set the output. */
*out = address;
R_SUCCEED();
}
Result VammManager::AllocateMemory(uintptr_t *out, size_t size) {
/* Allocate an address. */
uintptr_t address;
{
/* Lock access to our region manager. */
std::scoped_lock lk(m_lock);
AMS_ASSERT(m_region_manager != nullptr);
/* Allocate an address region. */
AddressRegion *region;
switch (m_region_manager->Allocate(std::addressof(region), size)) {
case AddressAllocationResult_Success:
address = region->GetAddressBegin();
break;
case AddressAllocationResult_OutOfSpace:
R_THROW(os::ResultOutOfVirtualAddressSpace());
default:
R_THROW(os::ResultOutOfMemory());
}
ON_RESULT_FAILURE { m_region_manager->Free(region); };
/* Allocate memory at the region. */
R_TRY(VammManagerImpl::AllocatePhysicalMemoryImpl(address, size));
}
/* Set the output. */
*out = address;
R_SUCCEED();
}
Result VammManager::AllocateMemoryPages(uintptr_t address, size_t size) {
/* Acquire read access to our region manager. */
std::shared_lock lk(m_lock);
AMS_ASSERT(m_region_manager != nullptr);
/* Check that the region was previously allocated by a call to AllocateAddressRegion. */
R_UNLESS(m_region_manager->IsAlreadyAllocated(address, size), os::ResultInvalidParameter());
/* Allocate the memory. */
R_RETURN(VammManagerImpl::AllocatePhysicalMemoryImpl(address, size));
}
Result VammManager::FreeAddressRegion(uintptr_t address) {
/* Lock access to our region manager. */
std::scoped_lock lk(m_lock);
AMS_ASSERT(m_region_manager != nullptr);
/* Verify the region can be freed. */
auto *region = m_region_manager->Find(address);
R_UNLESS(region != nullptr, os::ResultInvalidParameter());
/* Free any memory present at the address. */
R_TRY(VammManagerImpl::FreePhysicalMemoryImpl(address, region->GetSize()));
/* Free the region. */
m_region_manager->Free(region);
R_SUCCEED();
}
Result VammManager::FreeMemoryPages(uintptr_t address, size_t size) {
/* Acquire read access to our region manager. */
std::shared_lock lk(m_lock);
AMS_ASSERT(m_region_manager != nullptr);
/* Check that the region was previously allocated by a call to AllocateAddressRegion. */
R_UNLESS(m_region_manager->IsAlreadyAllocated(address, size), os::ResultInvalidParameter());
/* Free the memory. */
R_RETURN(VammManagerImpl::FreePhysicalMemoryImpl(address, size));
}
VirtualAddressMemoryResourceUsage VammManager::GetVirtualAddressMemoryResourceUsage() {
const size_t assigned_size = VammManagerImpl::GetExtraSystemResourceAssignedSize();
const size_t used_size = VammManagerImpl::GetExtraSystemResourceUsedSize();
/* Decide on an actual used size. */
const size_t reported_used_size = std::min<size_t>(assigned_size, used_size + 512_KB);
return VirtualAddressMemoryResourceUsage {
.assigned_size = assigned_size,
.used_size = reported_used_size,
};
}
bool VammManager::IsVirtualAddressMemoryEnabled() {
return VammManagerImpl::IsVirtualAddressMemoryEnabled();
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 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_resource_manager.hpp"
namespace ams::os::impl {
ALWAYS_INLINE VammManager &GetVammManager() {
return GetResourceManager().GetVammManager();
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 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 VammManagerHorizonImpl {
public:
static void GetReservedRegionImpl(uintptr_t *out_start, uintptr_t *out_size) {
u64 start, size;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(start), svc::InfoType_AliasRegionAddress, svc::PseudoHandle::CurrentProcess, 0));
R_ABORT_UNLESS(svc::GetInfo(std::addressof(size), svc::InfoType_AliasRegionSize, svc::PseudoHandle::CurrentProcess, 0));
*out_start = start;
*out_size = size;
}
static Result AllocatePhysicalMemoryImpl(uintptr_t address, size_t size) {
R_TRY_CATCH(svc::MapPhysicalMemory(address, size)) {
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
}
static Result FreePhysicalMemoryImpl(uintptr_t address, size_t size) {
R_TRY_CATCH(svc::UnmapPhysicalMemory(address, size)) {
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultBusy())
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory())
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
}
static size_t GetExtraSystemResourceAssignedSize() {
u64 v;
return R_SUCCEEDED(svc::GetInfo(std::addressof(v), svc::InfoType_SystemResourceSizeTotal, svc::PseudoHandle::CurrentProcess, 0)) ? v : 0;
}
static size_t GetExtraSystemResourceUsedSize() {
u64 v;
return R_SUCCEEDED(svc::GetInfo(std::addressof(v), svc::InfoType_SystemResourceSizeUsed, svc::PseudoHandle::CurrentProcess, 0)) ? v : 0;
}
static bool IsVirtualAddressMemoryEnabled() {
return GetExtraSystemResourceAssignedSize() > 0;
}
};
using VammManagerImpl = VammManagerHorizonImpl;
}

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