mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-14 11:01:01 +02:00
Merge pull request #2602 from Sonicadvance1/move_thread_creation
Threads: Moves pthread logic to FEXLoader
This commit is contained in:
7 files changed
+259
-195
No files matched your search
+6
-183
@@ -17,196 +17,19 @@
|
||||
#endif
|
||||
|
||||
namespace FEXCore::Threads {
|
||||
// Stack pool handling
|
||||
struct StackPoolItem {
|
||||
void *Ptr;
|
||||
size_t Size;
|
||||
};
|
||||
std::mutex DeadStackPoolMutex{};
|
||||
std::mutex LiveStackPoolMutex{};
|
||||
|
||||
static fextl::deque<StackPoolItem> DeadStackPool{};
|
||||
static fextl::deque<StackPoolItem> LiveStackPool{};
|
||||
|
||||
void *AllocateStackObject(size_t Size) {
|
||||
std::lock_guard lk{DeadStackPoolMutex};
|
||||
if (DeadStackPool.size() == 0) {
|
||||
// Nothing in the pool, just allocate
|
||||
return FEXCore::Allocator::mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
|
||||
// Keep the first item in the stack pool
|
||||
auto Result = DeadStackPool.front().Ptr;
|
||||
DeadStackPool.pop_front();
|
||||
|
||||
// Erase the rest as a garbage collection step
|
||||
for (auto &Item : DeadStackPool) {
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void AddStackToDeadPool(void *Ptr, size_t Size) {
|
||||
std::lock_guard lk{DeadStackPoolMutex};
|
||||
DeadStackPool.emplace_back(StackPoolItem{Ptr, Size});
|
||||
}
|
||||
|
||||
void AddStackToLivePool(void *Ptr, size_t Size) {
|
||||
std::lock_guard lk{LiveStackPoolMutex};
|
||||
LiveStackPool.emplace_back(StackPoolItem{Ptr, Size});
|
||||
}
|
||||
|
||||
void RemoveStackFromLivePool(void *Ptr) {
|
||||
std::lock_guard lk{LiveStackPoolMutex};
|
||||
for (auto it = LiveStackPool.begin(); it != LiveStackPool.end(); ++it) {
|
||||
if (it->Ptr == Ptr) {
|
||||
LiveStackPool.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeallocateStackObject(void *Ptr, size_t Size) {
|
||||
RemoveStackFromLivePool(Ptr);
|
||||
AddStackToDeadPool(Ptr, Size);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
std::lock_guard lk{DeadStackPoolMutex};
|
||||
std::lock_guard lk2{LiveStackPoolMutex};
|
||||
// Erase all the dead stack pools
|
||||
for (auto &Item : DeadStackPool) {
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
}
|
||||
|
||||
// Now clean up any that are considered to still be live
|
||||
// We are in shutdown phase, everything in the process is dead
|
||||
for (auto &Item : LiveStackPool) {
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
}
|
||||
|
||||
DeadStackPool.clear();
|
||||
LiveStackPool.clear();
|
||||
}
|
||||
|
||||
void *InitializeThread(void *Ptr);
|
||||
|
||||
class PThread final : public Thread {
|
||||
public:
|
||||
PThread(FEXCore::Threads::ThreadFunc Func, void *Arg)
|
||||
: UserFunc {Func}
|
||||
, UserArg {Arg} {
|
||||
pthread_attr_t Attr{};
|
||||
Stack = AllocateStackObject(STACK_SIZE);
|
||||
// pthreads allocates its dtv region behind our back and there is nothing we can do about it.
|
||||
FEXCore::Allocator::YesIKnowImNotSupposedToUseTheGlibcAllocator glibc;
|
||||
AddStackToLivePool(Stack, STACK_SIZE);
|
||||
pthread_attr_init(&Attr);
|
||||
pthread_attr_setstack(&Attr, Stack, STACK_SIZE);
|
||||
// TODO: Thread creation should be using this instead.
|
||||
// Causes Steam to crash early though.
|
||||
// pthread_create(&Thread, &Attr, InitializeThread, this);
|
||||
pthread_create(&Thread, &Attr, Func, Arg);
|
||||
|
||||
pthread_attr_destroy(&Attr);
|
||||
}
|
||||
|
||||
bool joinable() override {
|
||||
pthread_attr_t Attr{};
|
||||
if (pthread_getattr_np(Thread, &Attr) == 0) {
|
||||
int AttachState{};
|
||||
if (pthread_attr_getdetachstate(&Attr, &AttachState) == 0) {
|
||||
if (AttachState == PTHREAD_CREATE_JOINABLE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool join(void **ret) override {
|
||||
return pthread_join(Thread, ret) == 0;
|
||||
}
|
||||
|
||||
bool detach() override {
|
||||
return pthread_detach(Thread) == 0;
|
||||
}
|
||||
|
||||
bool IsSelf() override {
|
||||
auto self = pthread_self();
|
||||
return self == Thread;
|
||||
}
|
||||
|
||||
void *Execute() {
|
||||
return UserFunc(UserArg);
|
||||
}
|
||||
|
||||
void FreeStack() {
|
||||
DeallocateStackObject(Stack, STACK_SIZE);
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_t Thread;
|
||||
FEXCore::Threads::ThreadFunc UserFunc;
|
||||
void *UserArg;
|
||||
void *Stack{};
|
||||
constexpr static size_t STACK_SIZE = 8 * 1024 * 1024;
|
||||
};
|
||||
|
||||
void *InitializeThread(void *Ptr) {
|
||||
PThread *Thread{reinterpret_cast<PThread*>(Ptr)};
|
||||
|
||||
// Run the user function
|
||||
void *Result = Thread->Execute();
|
||||
|
||||
// Put the stack back in to the stack pool
|
||||
Thread->FreeStack();
|
||||
|
||||
// TLS/DTV teardown is something FEX can't control. Disable glibc checking when we leave a pthread.
|
||||
FEXCore::Allocator::YesIKnowImNotSupposedToUseTheGlibcAllocator::HardDisable();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
fextl::unique_ptr<FEXCore::Threads::Thread> CreateThread_PThread(
|
||||
fextl::unique_ptr<FEXCore::Threads::Thread> CreateThread_Default(
|
||||
ThreadFunc Func,
|
||||
void* Arg) {
|
||||
return fextl::make_unique<PThread>(Func, Arg);
|
||||
ERROR_AND_DIE_FMT("Frontend didn't setup thread creation!");
|
||||
}
|
||||
|
||||
void CleanupAfterFork_PThread() {
|
||||
// We don't need to pull the mutex here
|
||||
// After a fork we are the only thread running
|
||||
// Just need to make sure not to delete our own stack
|
||||
uintptr_t StackLocation = reinterpret_cast<uintptr_t>(alloca(0));
|
||||
|
||||
auto ClearStackPool = [&](auto &StackPool) {
|
||||
for (auto it = StackPool.begin(); it != StackPool.end(); ) {
|
||||
StackPoolItem &Item = *it;
|
||||
uintptr_t ItemStack = reinterpret_cast<uintptr_t>(Item.Ptr);
|
||||
if (ItemStack <= StackLocation && (ItemStack + Item.Size) > StackLocation) {
|
||||
// This is our stack item, skip it
|
||||
++it;
|
||||
}
|
||||
else {
|
||||
// Untracked stack. Clean it up
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
it = StackPool.erase(it);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Clear both dead stacks and live stacks
|
||||
ClearStackPool(DeadStackPool);
|
||||
ClearStackPool(LiveStackPool);
|
||||
|
||||
LogMan::Throw::AFmt((DeadStackPool.size() + LiveStackPool.size()) <= 1,
|
||||
"After fork we should only have zero or one tracked stacks!");
|
||||
void CleanupAfterFork_Default() {
|
||||
ERROR_AND_DIE_FMT("Frontend didn't setup thread creation!");
|
||||
}
|
||||
|
||||
static FEXCore::Threads::Pointers Ptrs = {
|
||||
.CreateThread = CreateThread_PThread,
|
||||
.CleanupAfterFork = CleanupAfterFork_PThread,
|
||||
.CreateThread = CreateThread_Default,
|
||||
.CleanupAfterFork = CleanupAfterFork_Default,
|
||||
};
|
||||
|
||||
fextl::unique_ptr<FEXCore::Threads::Thread> FEXCore::Threads::Thread::Create(
|
||||
|
||||
@@ -40,10 +40,6 @@ namespace FEXCore::Threads {
|
||||
static void SetInternalPointers(Pointers const &_Ptrs);
|
||||
};
|
||||
|
||||
void *AllocateStackObject(size_t Size);
|
||||
void DeallocateStackObject(void *Ptr, size_t Size);
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* @brief Sets the calling thread's signal mask to the one provided
|
||||
*
|
||||
|
||||
@@ -12,6 +12,7 @@ $end_info$
|
||||
#include "VDSO_Emulation.h"
|
||||
#include "LinuxSyscalls/LinuxAllocator.h"
|
||||
#include "LinuxSyscalls/Syscalls.h"
|
||||
#include "LinuxSyscalls/Utils/Threads.h"
|
||||
#include "LinuxSyscalls/x32/Syscalls.h"
|
||||
#include "LinuxSyscalls/x64/Syscalls.h"
|
||||
#include "LinuxSyscalls/SignalDelegator.h"
|
||||
@@ -387,6 +388,10 @@ int main(int argc, char **argv, char **const envp) {
|
||||
}
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.ProgramName);
|
||||
}
|
||||
|
||||
// Setup Thread handlers, so FEXCore can create threads.
|
||||
FEX::LinuxEmulation::Threads::SetupThreadHandlers();
|
||||
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
||||
|
||||
fextl::unique_ptr<FEX::HLE::MemAllocator> Allocator;
|
||||
@@ -546,7 +551,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
SyscallHandler.reset();
|
||||
SignalDelegation.reset();
|
||||
|
||||
FEXCore::Threads::Shutdown();
|
||||
FEX::LinuxEmulation::Threads::Shutdown();
|
||||
|
||||
Loader.FreeSections();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ add_library(LinuxEmulation STATIC
|
||||
Syscalls.cpp
|
||||
SyscallsSMCTracking.cpp
|
||||
SyscallsVMATracking.cpp
|
||||
Utils/Threads.cpp
|
||||
x32/Syscalls.cpp
|
||||
x32/EPoll.cpp
|
||||
x32/FD.cpp
|
||||
|
||||
@@ -13,6 +13,7 @@ $end_info$
|
||||
#include "LinuxSyscalls/SignalDelegator.h"
|
||||
#include "LinuxSyscalls/Syscalls.h"
|
||||
#include "LinuxSyscalls/Syscalls/Thread.h"
|
||||
#include "LinuxSyscalls/Utils/Threads.h"
|
||||
#include "LinuxSyscalls/x32/Syscalls.h"
|
||||
#include "LinuxSyscalls/x64/Syscalls.h"
|
||||
#include "LinuxSyscalls/x32/Types.h"
|
||||
@@ -29,7 +30,6 @@ $end_info$
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
#include <FEXCore/Utils/Threads.h>
|
||||
#include <FEXCore/Utils/FileLoading.h>
|
||||
#include <FEXCore/fextl/fmt.h>
|
||||
#include <FEXCore/fextl/sstream.h>
|
||||
@@ -426,7 +426,7 @@ struct StackFramePlusRet {
|
||||
static void Clone3HandlerRet() {
|
||||
StackFrameData *Data = (StackFrameData*)alloca(0);
|
||||
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
|
||||
FEXCore::Threads::DeallocateStackObject(Data->NewStack, Data->StackSize);
|
||||
FEX::LinuxEmulation::Threads::DeallocateStackObject(Data->NewStack);
|
||||
// To behave like a real clone, we now just need to call exit here
|
||||
exit(Result);
|
||||
FEX_UNREACHABLE;
|
||||
@@ -435,7 +435,7 @@ static void Clone3HandlerRet() {
|
||||
static int Clone2HandlerRet(void *arg) {
|
||||
StackFrameData *Data = (StackFrameData*)arg;
|
||||
uint64_t Result = FEX::HLE::HandleNewClone(Data->Thread, Data->CTX, &Data->NewFrame, &Data->GuestArgs);
|
||||
FEXCore::Threads::DeallocateStackObject(Data->NewStack, Data->StackSize);
|
||||
FEX::LinuxEmulation::Threads::DeallocateStackObject(Data->NewStack);
|
||||
FEXCore::Allocator::free(arg);
|
||||
return Result;
|
||||
}
|
||||
@@ -489,8 +489,8 @@ static uint64_t Clone2Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clo
|
||||
Data->GuestArgs = *args;
|
||||
|
||||
// In the case of thread, we need a new stack
|
||||
Data->StackSize = 8 * 1024 * 1024;
|
||||
Data->NewStack = FEXCore::Threads::AllocateStackObject(Data->StackSize);
|
||||
Data->StackSize = FEX::LinuxEmulation::Threads::STACK_SIZE;
|
||||
Data->NewStack = FEX::LinuxEmulation::Threads::AllocateStackObject();
|
||||
|
||||
// Create a copy of the parent frame
|
||||
memcpy(&Data->NewFrame, Frame, sizeof(FEXCore::Core::CpuStateFrame));
|
||||
@@ -514,8 +514,8 @@ static uint64_t Clone2Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clo
|
||||
|
||||
static uint64_t Clone3Handler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) {
|
||||
// In the case of thread, we need a new stack
|
||||
uint64_t StackSize = 8 * 1024 * 1024;
|
||||
void *NewStack = FEXCore::Threads::AllocateStackObject(StackSize);
|
||||
const uint64_t StackSize = FEX::LinuxEmulation::Threads::STACK_SIZE;
|
||||
void *NewStack = FEX::LinuxEmulation::Threads::AllocateStackObject();
|
||||
|
||||
constexpr size_t Offset = sizeof(StackFramePlusRet);
|
||||
StackFramePlusRet *Data = (StackFramePlusRet*)(reinterpret_cast<uint64_t>(NewStack) + StackSize - Offset);
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "LinuxSyscalls/Utils/Threads.h"
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Utils/Threads.h>
|
||||
|
||||
#include <FEXCore/fextl/deque.h>
|
||||
|
||||
namespace FEX::LinuxEmulation::Threads {
|
||||
// Stack pool handling
|
||||
struct StackPoolItem {
|
||||
void *Ptr;
|
||||
size_t Size;
|
||||
};
|
||||
std::mutex DeadStackPoolMutex{};
|
||||
std::mutex LiveStackPoolMutex{};
|
||||
|
||||
static fextl::deque<StackPoolItem> DeadStackPool{};
|
||||
static fextl::deque<StackPoolItem> LiveStackPool{};
|
||||
|
||||
void *AllocateStackObject() {
|
||||
std::lock_guard lk{DeadStackPoolMutex};
|
||||
if (DeadStackPool.size() == 0) {
|
||||
// Nothing in the pool, just allocate
|
||||
return FEXCore::Allocator::mmap(nullptr, FEX::LinuxEmulation::Threads::STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
}
|
||||
|
||||
// Keep the first item in the stack pool
|
||||
auto Result = DeadStackPool.front().Ptr;
|
||||
DeadStackPool.pop_front();
|
||||
|
||||
// Erase the rest as a garbage collection step
|
||||
for (auto &Item : DeadStackPool) {
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void AddStackToDeadPool(void *Ptr) {
|
||||
std::lock_guard lk{DeadStackPoolMutex};
|
||||
DeadStackPool.emplace_back(StackPoolItem{Ptr, FEX::LinuxEmulation::Threads::STACK_SIZE});
|
||||
}
|
||||
|
||||
void AddStackToLivePool(void *Ptr) {
|
||||
std::lock_guard lk{LiveStackPoolMutex};
|
||||
LiveStackPool.emplace_back(StackPoolItem{Ptr, FEX::LinuxEmulation::Threads::STACK_SIZE});
|
||||
}
|
||||
|
||||
void RemoveStackFromLivePool(void *Ptr) {
|
||||
std::lock_guard lk{LiveStackPoolMutex};
|
||||
for (auto it = LiveStackPool.begin(); it != LiveStackPool.end(); ++it) {
|
||||
if (it->Ptr == Ptr) {
|
||||
LiveStackPool.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeallocateStackObject(void *Ptr) {
|
||||
RemoveStackFromLivePool(Ptr);
|
||||
AddStackToDeadPool(Ptr);
|
||||
}
|
||||
|
||||
namespace PThreads {
|
||||
void *InitializeThread(void *Ptr);
|
||||
|
||||
class PThread final : public FEXCore::Threads::Thread {
|
||||
public:
|
||||
PThread(FEXCore::Threads::ThreadFunc Func, void *Arg)
|
||||
: UserFunc {Func}
|
||||
, UserArg {Arg} {
|
||||
pthread_attr_t Attr{};
|
||||
Stack = AllocateStackObject();
|
||||
// pthreads allocates its dtv region behind our back and there is nothing we can do about it.
|
||||
FEXCore::Allocator::YesIKnowImNotSupposedToUseTheGlibcAllocator glibc;
|
||||
AddStackToLivePool(Stack);
|
||||
pthread_attr_init(&Attr);
|
||||
pthread_attr_setstack(&Attr, Stack, FEX::LinuxEmulation::Threads::STACK_SIZE);
|
||||
// TODO: Thread creation should be using this instead.
|
||||
// Causes Steam to crash early though.
|
||||
// pthread_create(&Thread, &Attr, InitializeThread, this);
|
||||
pthread_create(&Thread, &Attr, Func, Arg);
|
||||
|
||||
pthread_attr_destroy(&Attr);
|
||||
}
|
||||
|
||||
bool joinable() override {
|
||||
pthread_attr_t Attr{};
|
||||
if (pthread_getattr_np(Thread, &Attr) == 0) {
|
||||
int AttachState{};
|
||||
if (pthread_attr_getdetachstate(&Attr, &AttachState) == 0) {
|
||||
if (AttachState == PTHREAD_CREATE_JOINABLE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool join(void **ret) override {
|
||||
return pthread_join(Thread, ret) == 0;
|
||||
}
|
||||
|
||||
bool detach() override {
|
||||
return pthread_detach(Thread) == 0;
|
||||
}
|
||||
|
||||
bool IsSelf() override {
|
||||
auto self = pthread_self();
|
||||
return self == Thread;
|
||||
}
|
||||
|
||||
void *Execute() {
|
||||
return UserFunc(UserArg);
|
||||
}
|
||||
|
||||
void FreeStack() {
|
||||
DeallocateStackObject(Stack);
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_t Thread;
|
||||
FEXCore::Threads::ThreadFunc UserFunc;
|
||||
void *UserArg;
|
||||
void *Stack{};
|
||||
};
|
||||
|
||||
void *InitializeThread(void *Ptr) {
|
||||
PThread *Thread{reinterpret_cast<PThread*>(Ptr)};
|
||||
|
||||
// Run the user function
|
||||
void *Result = Thread->Execute();
|
||||
|
||||
// Put the stack back in to the stack pool
|
||||
Thread->FreeStack();
|
||||
|
||||
// TLS/DTV teardown is something FEX can't control. Disable glibc checking when we leave a pthread.
|
||||
FEXCore::Allocator::YesIKnowImNotSupposedToUseTheGlibcAllocator::HardDisable();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
fextl::unique_ptr<FEXCore::Threads::Thread> CreateThread_PThread(
|
||||
FEXCore::Threads::ThreadFunc Func,
|
||||
void* Arg) {
|
||||
return fextl::make_unique<PThread>(Func, Arg);
|
||||
}
|
||||
|
||||
void CleanupAfterFork_PThread() {
|
||||
// We don't need to pull the mutex here
|
||||
// After a fork we are the only thread running
|
||||
// Just need to make sure not to delete our own stack
|
||||
uintptr_t StackLocation = reinterpret_cast<uintptr_t>(alloca(0));
|
||||
|
||||
auto ClearStackPool = [&](auto &StackPool) {
|
||||
for (auto it = StackPool.begin(); it != StackPool.end(); ) {
|
||||
StackPoolItem &Item = *it;
|
||||
uintptr_t ItemStack = reinterpret_cast<uintptr_t>(Item.Ptr);
|
||||
if (ItemStack <= StackLocation && (ItemStack + Item.Size) > StackLocation) {
|
||||
// This is our stack item, skip it
|
||||
++it;
|
||||
}
|
||||
else {
|
||||
// Untracked stack. Clean it up
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
it = StackPool.erase(it);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Clear both dead stacks and live stacks
|
||||
ClearStackPool(DeadStackPool);
|
||||
ClearStackPool(LiveStackPool);
|
||||
|
||||
LogMan::Throw::AFmt((DeadStackPool.size() + LiveStackPool.size()) <= 1,
|
||||
"After fork we should only have zero or one tracked stacks!");
|
||||
}
|
||||
};
|
||||
|
||||
void SetupThreadHandlers() {
|
||||
FEXCore::Threads::Pointers Ptrs = {
|
||||
.CreateThread = PThreads::CreateThread_PThread,
|
||||
.CleanupAfterFork = PThreads::CleanupAfterFork_PThread,
|
||||
};
|
||||
|
||||
FEXCore::Threads::Thread::SetInternalPointers(Ptrs);
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
std::lock_guard lk{DeadStackPoolMutex};
|
||||
std::lock_guard lk2{LiveStackPoolMutex};
|
||||
// Erase all the dead stack pools
|
||||
for (auto &Item : DeadStackPool) {
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
}
|
||||
|
||||
// Now clean up any that are considered to still be live
|
||||
// We are in shutdown phase, everything in the process is dead
|
||||
for (auto &Item : LiveStackPool) {
|
||||
FEXCore::Allocator::munmap(Item.Ptr, Item.Size);
|
||||
}
|
||||
|
||||
DeadStackPool.clear();
|
||||
LiveStackPool.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace FEX::LinuxEmulation::Threads {
|
||||
/**
|
||||
* @brief Size of the stack that this interface creates.
|
||||
*/
|
||||
constexpr size_t STACK_SIZE = 8 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* @brief Allocates a stack object from the internally managed stack pool.
|
||||
*/
|
||||
void *AllocateStackObject();
|
||||
|
||||
/**
|
||||
* @brief Deallocates a stack from the internally managed stack pool.
|
||||
*
|
||||
* Will not free the memory immediately, instead saving for reuse temporarily to solve race conditions on stack usage while stack tears down.
|
||||
*
|
||||
* @param Ptr The stack base from `AllocateStackObject`
|
||||
*/
|
||||
void DeallocateStackObject(void *Ptr);
|
||||
|
||||
/**
|
||||
* @brief Registers thread creation handlers with FEXCore.
|
||||
*/
|
||||
void SetupThreadHandlers();
|
||||
|
||||
/**
|
||||
* @brief Cleans up any remaining stack objects in the pools.
|
||||
*/
|
||||
void Shutdown();
|
||||
}
|
||||
Reference in new issue
Block a user