util::unique_lock, update loader to new sf semantics

This commit is contained in:
Michael Scire 2021-01-18 06:18:14 -08:00 committed by SciresM
parent 3761f80592
commit e4e278bb3d
9 changed files with 262 additions and 39 deletions

View file

@ -42,9 +42,9 @@ namespace ams::fssrv::impl {
private: private:
ams::sf::SharedPointer<FileSystemInterfaceAdapter> parent_filesystem; ams::sf::SharedPointer<FileSystemInterfaceAdapter> parent_filesystem;
std::unique_ptr<fs::fsa::IFile> base_file; std::unique_ptr<fs::fsa::IFile> base_file;
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore; util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
public: public:
FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, std::unique_lock<fssystem::SemaphoreAdapter> &&sema); FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema);
~FileInterfaceAdapter(); ~FileInterfaceAdapter();
private: private:
void InvalidateCache(); void InvalidateCache();
@ -64,9 +64,9 @@ namespace ams::fssrv::impl {
private: private:
ams::sf::SharedPointer<FileSystemInterfaceAdapter> parent_filesystem; ams::sf::SharedPointer<FileSystemInterfaceAdapter> parent_filesystem;
std::unique_ptr<fs::fsa::IDirectory> base_dir; std::unique_ptr<fs::fsa::IDirectory> base_dir;
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore; util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
public: public:
DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, std::unique_lock<fssystem::SemaphoreAdapter> &&sema); DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema);
~DirectoryInterfaceAdapter(); ~DirectoryInterfaceAdapter();
public: public:
/* Command API */ /* Command API */
@ -79,7 +79,7 @@ namespace ams::fssrv::impl {
NON_COPYABLE(FileSystemInterfaceAdapter); NON_COPYABLE(FileSystemInterfaceAdapter);
private: private:
std::shared_ptr<fs::fsa::IFileSystem> base_fs; std::shared_ptr<fs::fsa::IFileSystem> base_fs;
std::unique_lock<fssystem::SemaphoreAdapter> mount_count_semaphore; util::unique_lock<fssystem::SemaphoreAdapter> mount_count_semaphore;
os::ReadWriteLock invalidation_lock; os::ReadWriteLock invalidation_lock;
bool open_count_limited; bool open_count_limited;
bool deep_retry_enabled = false; bool deep_retry_enabled = false;

View file

@ -32,7 +32,7 @@ namespace ams::fssrv::impl {
private: private:
/* TODO: Nintendo uses fssystem::AsynchronousAccessStorage here. */ /* TODO: Nintendo uses fssystem::AsynchronousAccessStorage here. */
std::shared_ptr<fs::IStorage> base_storage; std::shared_ptr<fs::IStorage> base_storage;
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore; util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
os::ReadWriteLock invalidation_lock; os::ReadWriteLock invalidation_lock;
/* TODO: DataStorageContext. */ /* TODO: DataStorageContext. */
bool deep_retry_enabled = false; bool deep_retry_enabled = false;

View file

@ -19,7 +19,7 @@
namespace ams::fssrv::impl { namespace ams::fssrv::impl {
FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, std::unique_lock<fssystem::SemaphoreAdapter> &&sema) FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema)
: parent_filesystem(parent, true), base_file(std::move(file)), open_count_semaphore(std::move(sema)) : parent_filesystem(parent, true), base_file(std::move(file)), open_count_semaphore(std::move(sema))
{ {
/* ... */ /* ... */
@ -89,7 +89,7 @@ namespace ams::fssrv::impl {
return ResultSuccess(); return ResultSuccess();
} }
DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, std::unique_lock<fssystem::SemaphoreAdapter> &&sema) DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema)
: parent_filesystem(parent, true), base_dir(std::move(dir)), open_count_semaphore(std::move(sema)) : parent_filesystem(parent, true), base_dir(std::move(dir)), open_count_semaphore(std::move(sema))
{ {
/* ... */ /* ... */
@ -236,7 +236,7 @@ namespace ams::fssrv::impl {
Result FileSystemInterfaceAdapter::OpenFile(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFile>> out, const fssrv::sf::Path &path, u32 mode) { Result FileSystemInterfaceAdapter::OpenFile(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFile>> out, const fssrv::sf::Path &path, u32 mode) {
auto read_lock = this->AcquireCacheInvalidationReadLock(); auto read_lock = this->AcquireCacheInvalidationReadLock();
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore; util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
if (this->open_count_limited) { if (this->open_count_limited) {
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
AMS_ABORT_UNLESS(false); AMS_ABORT_UNLESS(false);
@ -264,7 +264,7 @@ namespace ams::fssrv::impl {
Result FileSystemInterfaceAdapter::OpenDirectory(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDirectory>> out, const fssrv::sf::Path &path, u32 mode) { Result FileSystemInterfaceAdapter::OpenDirectory(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDirectory>> out, const fssrv::sf::Path &path, u32 mode) {
auto read_lock = this->AcquireCacheInvalidationReadLock(); auto read_lock = this->AcquireCacheInvalidationReadLock();
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore; util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
if (this->open_count_limited) { if (this->open_count_limited) {
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
AMS_ABORT_UNLESS(false); AMS_ABORT_UNLESS(false);

View file

@ -22,10 +22,10 @@ namespace ams::fssystem {
NON_COPYABLE(KeySlotCacheAccessor); NON_COPYABLE(KeySlotCacheAccessor);
NON_MOVEABLE(KeySlotCacheAccessor); NON_MOVEABLE(KeySlotCacheAccessor);
private: private:
std::unique_lock<os::Mutex> lk; util::unique_lock<os::Mutex> lk;
const s32 slot_index; const s32 slot_index;
public: public:
KeySlotCacheAccessor(s32 idx, std::unique_lock<os::Mutex> &&l) : lk(std::move(l)), slot_index(idx) { /* ... */ } KeySlotCacheAccessor(s32 idx, util::unique_lock<os::Mutex> &&l) : lk(std::move(l)), slot_index(idx) { /* ... */ }
s32 GetKeySlotIndex() const { return this->slot_index; } s32 GetKeySlotIndex() const { return this->slot_index; }
}; };
@ -79,7 +79,7 @@ namespace ams::fssystem {
} }
Result Find(std::unique_ptr<KeySlotCacheAccessor> *out, const void *key, size_t key_size, s32 key2) { Result Find(std::unique_ptr<KeySlotCacheAccessor> *out, const void *key, size_t key_size, s32 key2) {
std::unique_lock lk(this->mutex); util::unique_lock lk(this->mutex);
KeySlotCacheEntryList *lists[2] = { std::addressof(this->high_priority_mru_list), std::addressof(this->low_priority_mru_list) }; KeySlotCacheEntryList *lists[2] = { std::addressof(this->high_priority_mru_list), std::addressof(this->low_priority_mru_list) };
for (auto list : lists) { for (auto list : lists) {
@ -100,12 +100,12 @@ namespace ams::fssystem {
} }
void AddEntry(KeySlotCacheEntry *entry) { void AddEntry(KeySlotCacheEntry *entry) {
std::unique_lock lk(this->mutex); util::unique_lock lk(this->mutex);
this->low_priority_mru_list.push_front(*entry); this->low_priority_mru_list.push_front(*entry);
} }
private: private:
Result AllocateFromLru(std::unique_ptr<KeySlotCacheAccessor> *out, KeySlotCacheEntryList &dst_list, const void *key, size_t key_size, s32 key2) { Result AllocateFromLru(std::unique_ptr<KeySlotCacheAccessor> *out, KeySlotCacheEntryList &dst_list, const void *key, size_t key_size, s32 key2) {
std::unique_lock lk(this->mutex); util::unique_lock lk(this->mutex);
KeySlotCacheEntryList &src_list = this->low_priority_mru_list.empty() ? this->high_priority_mru_list : this->low_priority_mru_list; KeySlotCacheEntryList &src_list = this->low_priority_mru_list.empty() ? this->high_priority_mru_list : this->low_priority_mru_list;
AMS_ASSERT(!src_list.empty()); AMS_ASSERT(!src_list.empty());

View file

@ -41,6 +41,7 @@
#include <vapours/util/util_overlap.hpp> #include <vapours/util/util_overlap.hpp>
#include <vapours/util/util_string_util.hpp> #include <vapours/util/util_string_util.hpp>
#include <vapours/util/util_string_view.hpp> #include <vapours/util/util_string_view.hpp>
#include <vapours/util/util_mutex_utils.hpp>
#include <vapours/util/util_variadic.hpp> #include <vapours/util/util_variadic.hpp>
#include <vapours/util/util_character_encoding.hpp> #include <vapours/util/util_character_encoding.hpp>
#include <vapours/util/util_format_string.hpp> #include <vapours/util/util_format_string.hpp>

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours/common.hpp>
#include <vapours/assert.hpp>
namespace ams::util {
template<typename _Mutex>
class unique_lock {
NON_COPYABLE(unique_lock);
public:
using mutex_type = _Mutex;
private:
mutex_type *m_mutex;
bool m_owns;
public:
unique_lock() noexcept : m_mutex(nullptr), m_owns(false) { /* ... */ }
explicit unique_lock(mutex_type &m) noexcept : m_mutex(std::addressof(m)), m_owns(false) {
this->lock();
m_owns = true;
}
unique_lock(mutex_type &m, std::defer_lock_t) noexcept : m_mutex(std::addressof(m)), m_owns(false) { /* ... */ }
unique_lock(mutex_type &m, std::try_to_lock_t) noexcept : m_mutex(std::addressof(m)), m_owns(m_mutex->try_lock()) { /* ... */ }
unique_lock(mutex_type &m, std::adopt_lock_t) noexcept : m_mutex(std::addressof(m)), m_owns(true) { /* ... */ }
template<typename _Clock, typename _Duration>
unique_lock(mutex_type &m, const std::chrono::time_point<_Clock, _Duration> &time) noexcept : m_mutex(std::addressof(m)), m_owns(m_mutex->try_lock_until(time)) { /* ... */ }
template<typename _Rep, typename _Period>
unique_lock(mutex_type &m, const std::chrono::duration<_Rep, _Period> &time) noexcept : m_mutex(std::addressof(m)), m_owns(m_mutex->try_lock_for(time)) { /* ... */ }
~unique_lock() noexcept {
if (m_owns) {
this->unlock();
}
}
unique_lock(unique_lock &&rhs) noexcept : m_mutex(rhs.m_mutex), m_owns(rhs.m_owns) {
rhs.m_mutex = nullptr;
rhs.m_owns = false;
}
unique_lock &operator=(unique_lock &&rhs) noexcept {
if (m_owns) {
this->unlock();
}
unique_lock(std::move(rhs)).swap(*this);
rhs.m_mutex = nullptr;
rhs.m_owns = false;
return *this;
}
void lock() noexcept {
AMS_ABORT_UNLESS(m_mutex);
AMS_ABORT_UNLESS(!m_owns);
m_mutex->lock();
m_owns = true;
}
bool try_lock() noexcept {
AMS_ABORT_UNLESS(m_mutex);
AMS_ABORT_UNLESS(!m_owns);
m_owns = m_mutex->try_lock();
return m_owns;
}
template<typename _Clock, typename _Duration>
bool try_lock_until(const std::chrono::time_point<_Clock, _Duration> &time) noexcept {
AMS_ABORT_UNLESS(m_mutex);
AMS_ABORT_UNLESS(!m_owns);
m_owns = m_mutex->try_lock_until(time);
return m_owns;
}
template<typename _Rep, typename _Period>
bool try_lock_for(const std::chrono::duration<_Rep, _Period> &time) noexcept {
AMS_ABORT_UNLESS(m_mutex);
AMS_ABORT_UNLESS(!m_owns);
m_owns = m_mutex->try_lock_for(time);
return m_owns;
}
void unlock() noexcept {
AMS_ABORT_UNLESS(m_owns);
if (m_mutex) {
m_mutex->unlock();
m_owns = false;
}
}
void swap(unique_lock &rhs) noexcept {
std::swap(m_mutex, rhs.m_mutex);
std::swap(m_owns, rhs.m_owns);
}
mutex_type *release() noexcept {
mutex_type *ret = m_mutex;
m_mutex = nullptr;
m_owns = false;
return ret;
}
bool owns_lock() const noexcept {
return m_owns;
}
explicit operator bool() const noexcept {
return this->owns_lock();
}
mutex_type *mutex() const noexcept {
return m_mutex;
}
};
template<typename _Mutex>
inline void swap(unique_lock<_Mutex> &lhs, unique_lock<_Mutex> &rhs) noexcept { return lhs.swap(rhs); }
}

View file

@ -18,7 +18,7 @@
namespace ams::ldr { namespace ams::ldr {
class LoaderService final { class LoaderService {
public: public:
/* Official commands. */ /* Official commands. */
Result CreateProcess(sf::OutMoveHandle proc_h, PinId id, u32 flags, sf::CopyHandle reslimit_h); Result CreateProcess(sf::OutMoveHandle proc_h, PinId id, u32 flags, sf::CopyHandle reslimit_h);

View file

@ -23,7 +23,7 @@ extern "C" {
u32 __nx_applet_type = AppletType_None; u32 __nx_applet_type = AppletType_None;
u32 __nx_fs_num_sessions = 1; u32 __nx_fs_num_sessions = 1;
#define INNER_HEAP_SIZE 0x8000 #define INNER_HEAP_SIZE 0x0
size_t nx_inner_heap_size = INNER_HEAP_SIZE; size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE]; char nx_inner_heap[INNER_HEAP_SIZE];
@ -35,6 +35,9 @@ extern "C" {
alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx); void __libnx_exception_handler(ThreadExceptionDump *ctx);
void *__libnx_thread_alloc(size_t size);
void __libnx_thread_free(void *mem);
} }
namespace ams { namespace ams {
@ -55,6 +58,78 @@ void __libnx_exception_handler(ThreadExceptionDump *ctx) {
ams::CrashHandler(ctx); ams::CrashHandler(ctx);
} }
namespace ams::ldr {
namespace {
constinit u8 g_heap_memory[16_KB];
lmem::HeapHandle g_server_heap_handle;
constinit ams::sf::ExpHeapAllocator g_server_allocator;
void *Allocate(size_t size) {
return lmem::AllocateFromExpHeap(g_server_heap_handle, size);
}
void Deallocate(void *p, size_t size) {
return lmem::FreeToExpHeap(g_server_heap_handle, p);
}
void InitializeHeap() {
g_server_heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_None);
g_server_allocator.Attach(g_server_heap_handle);
}
}
namespace {
struct ServerOptions {
static constexpr size_t PointerBufferSize = 0x400;
static constexpr size_t MaxDomains = 0;
static constexpr size_t MaxDomainObjects = 0;
};
/* ldr:pm, ldr:shel, ldr:dmnt. */
enum PortIndex {
PortIndex_ProcessManager,
PortIndex_Shell,
PortIndex_DebugMonitor,
PortIndex_Count,
};
constexpr sm::ServiceName ProcessManagerServiceName = sm::ServiceName::Encode("ldr:pm");
constexpr size_t ProcessManagerMaxSessions = 1;
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("ldr:shel");
constexpr size_t ShellMaxSessions = 3;
constexpr sm::ServiceName DebugMonitorServiceName = sm::ServiceName::Encode("ldr:dmnt");
constexpr size_t DebugMonitorMaxSessions = 3;
constinit sf::UnmanagedServiceObject<impl::IProcessManagerInterface, LoaderService> g_pm_service;
constinit sf::UnmanagedServiceObject<impl::IShellInterface, LoaderService> g_shell_service;
constinit sf::UnmanagedServiceObject<impl::IDebugMonitorInterface, LoaderService> g_dmnt_service;
constexpr size_t MaxSessions = ProcessManagerMaxSessions + ShellMaxSessions + DebugMonitorMaxSessions + 1;
using ServerManager = ams::sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions>;
ServerManager g_server_manager;
void RegisterServiceSessions() {
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service.GetShared(), ProcessManagerServiceName, ProcessManagerMaxSessions));
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_shell_service.GetShared(), ShellServiceName, ShellMaxSessions));
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_dmnt_service.GetShared(), DebugMonitorServiceName, DebugMonitorMaxSessions));
}
void LoopProcess() {
g_server_manager.LoopProcess();
}
}
}
void __libnx_initheap(void) { void __libnx_initheap(void) {
void* addr = nx_inner_heap; void* addr = nx_inner_heap;
size_t size = nx_inner_heap_size; size_t size = nx_inner_heap_size;
@ -65,11 +140,15 @@ void __libnx_initheap(void) {
fake_heap_start = (char*)addr; fake_heap_start = (char*)addr;
fake_heap_end = (char*)addr + size; fake_heap_end = (char*)addr + size;
ams::ldr::InitializeHeap();
} }
void __appInit(void) { void __appInit(void) {
hos::InitializeForStratosphere(); hos::InitializeForStratosphere();
fs::SetAllocator(ldr::Allocate, ldr::Deallocate);
/* Initialize services we need. */ /* Initialize services we need. */
sm::DoWithSession([&]() { sm::DoWithSession([&]() {
R_ABORT_UNLESS(fsInitialize()); R_ABORT_UNLESS(fsInitialize());
@ -89,28 +168,32 @@ void __appExit(void) {
fsExit(); fsExit();
} }
namespace { namespace ams {
struct ServerOptions { void *Malloc(size_t size) {
static constexpr size_t PointerBufferSize = 0x400; AMS_ABORT("ams::Malloc was called");
static constexpr size_t MaxDomains = 0; }
static constexpr size_t MaxDomainObjects = 0;
};
constexpr sm::ServiceName ProcessManagerServiceName = sm::ServiceName::Encode("ldr:pm"); void Free(void *ptr) {
constexpr size_t ProcessManagerMaxSessions = 1; AMS_ABORT("ams::Free was called");
}
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("ldr:shel"); }
constexpr size_t ShellMaxSessions = 3;
constexpr sm::ServiceName DebugMonitorServiceName = sm::ServiceName::Encode("ldr:dmnt"); void *operator new(size_t size) {
constexpr size_t DebugMonitorMaxSessions = 3; return ldr::Allocate(size);
}
/* ldr:pm, ldr:shel, ldr:dmnt. */ void operator delete(void *p) {
constexpr size_t NumServers = 3; return ldr::Deallocate(p, 0);
constexpr size_t MaxSessions = ProcessManagerMaxSessions + ShellMaxSessions + DebugMonitorMaxSessions + 1; }
sf::hipc::ServerManager<NumServers, ServerOptions, MaxSessions> g_server_manager;
void *__libnx_thread_alloc(size_t size) {
AMS_ABORT("__libnx_thread_alloc was called");
}
void __libnx_thread_free(void *mem) {
AMS_ABORT("__libnx_thread_free was called");
} }
int main(int argc, char **argv) int main(int argc, char **argv)
@ -128,13 +211,11 @@ int main(int argc, char **argv)
ldr::SetDevelopmentForAntiDowngradeCheck(spl::IsDevelopment()); ldr::SetDevelopmentForAntiDowngradeCheck(spl::IsDevelopment());
ldr::SetDevelopmentForAcidSignatureCheck(spl::IsDevelopment()); ldr::SetDevelopmentForAcidSignatureCheck(spl::IsDevelopment());
/* Add services to manager. */ /* Register the loader services. */
R_ABORT_UNLESS((g_server_manager.RegisterServer<ldr::impl::IProcessManagerInterface, ldr::LoaderService>(ProcessManagerServiceName, ProcessManagerMaxSessions))); ldr::RegisterServiceSessions();
R_ABORT_UNLESS((g_server_manager.RegisterServer<ldr::impl::IShellInterface, ldr::LoaderService>(ShellServiceName, ShellMaxSessions)));
R_ABORT_UNLESS((g_server_manager.RegisterServer<ldr::impl::IDebugMonitorInterface, ldr::LoaderService>(DebugMonitorServiceName, DebugMonitorMaxSessions)));
/* Loop forever, servicing our services. */ /* Loop forever, servicing our services. */
g_server_manager.LoopProcess(); ldr::LoopProcess();
return 0; return 0;
} }

View file

@ -654,7 +654,7 @@ namespace ams::ldr {
fssystem::DestroyExternalCode(loc.program_id); fssystem::DestroyExternalCode(loc.program_id);
/* Note that we've created the program. */ /* Note that we've created the program. */
SetLaunchedProgram(loc.program_id); SetLaunchedBootProgram(loc.program_id);
/* Move the process handle to output. */ /* Move the process handle to output. */
*out = info.process_handle.Move(); *out = info.process_handle.Move();