ams: support building unit test programs on windows/linux/macos
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
27
libraries/libstratosphere/source/os/impl/os_giant_lock.hpp
Normal file
27
libraries/libstratosphere/source/os/impl/os_giant_lock.hpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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); */
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
142
libraries/libstratosphere/source/os/impl/os_tls_manager.cpp
Normal file
142
libraries/libstratosphere/source/os/impl/os_tls_manager.cpp
Normal 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
|
||||
|
||||
}
|
||||
27
libraries/libstratosphere/source/os/impl/os_tls_manager.hpp
Normal file
27
libraries/libstratosphere/source/os/impl/os_tls_manager.hpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
397
libraries/libstratosphere/source/os/impl/os_vamm_manager.cpp
Normal file
397
libraries/libstratosphere/source/os/impl/os_vamm_manager.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
26
libraries/libstratosphere/source/os/impl/os_vamm_manager.hpp
Normal file
26
libraries/libstratosphere/source/os/impl/os_vamm_manager.hpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user