forked from FEX-Emu/FEX
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathThreadManager.h
More file actions
272 lines (214 loc) · 8.47 KB
/
ThreadManager.h
File metadata and controls
272 lines (214 loc) · 8.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|ThreadManager
desc: Frontend thread management
$end_info$
*/
#pragma once
#include "Common/SHMStats.h"
#include "LinuxSyscalls/Types.h"
#include "LinuxSyscalls/x32/IoctlEmulation.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/Context.h>
#include <FEXCore/fextl/vector.h>
#include <FEXCore/Utils/InterruptableConditionVariable.h>
#include <FEXCore/Utils/Profiler.h>
#include <FEXCore/Utils/SignalScopeGuards.h>
#include <FEXCore/Utils/Threads.h>
#include <FEXCore/Utils/TypeDefines.h>
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <optional>
#include <sys/stat.h>
#include <bits/types/sigset_t.h>
#include <linux/seccomp.h>
namespace FEX::HLE {
class SignalDelegator;
class SyscallHandler;
struct SeccompFilterInfo;
enum class SignalEvent : uint32_t {
Nothing, // If the guest uses our signal we need to know it was errant on our end
Pause,
Stop,
Return,
ReturnRT,
};
struct ThreadStateObject : public FEXCore::Allocator::FEXAllocOperators {
struct DeferredSignalState {
siginfo_t Info;
int Signal;
uint64_t SigMask;
};
FEXCore::Core::InternalThreadState* Thread;
struct {
uint32_t parent_tid;
uint32_t PID;
std::atomic<uint32_t> TID;
int32_t* set_child_tid {0};
int32_t* clear_child_tid {0};
uint64_t robust_list_head {0};
} ThreadInfo {};
struct {
SignalDelegator* Delegator {};
void* AltStackPtr {};
stack_t GuestAltStack {
.ss_sp = nullptr,
.ss_flags = SS_DISABLE, // By default the guest alt stack is disabled
.ss_size = 0,
};
// This is the thread's current signal mask
FEX::HLE::GuestSAMask CurrentSignalMask {};
// The mask prior to a suspend
FEX::HLE::GuestSAMask PreviousSuspendMask {};
uint64_t PendingSignals {};
// Queue of thread local signal frames that have been deferred.
// Async signals aren't guaranteed to be delivered in any particular order, but FEX treats them as FILO.
fextl::vector<DeferredSignalState> DeferredSignalFrames;
} SignalInfo {};
// Seccomp thread specific data.
uint32_t SeccompMode {SECCOMP_MODE_DISABLED};
fextl::vector<FEX::HLE::SeccompFilterInfo*> Filters {};
// personality emulation.
uint32_t persona {};
FEXCore::Core::NonMovableUniquePtr<FEXCore::Threads::Thread> ExecutionThread;
// Thread signaling information
std::atomic<SignalEvent> SignalReason {SignalEvent::Nothing};
// Thread pause handling
std::atomic_bool ThreadSleeping {false};
FEXCore::InterruptableConditionVariable ThreadPaused;
// GDB signal information
struct GdbInfoStruct {
int Signal {};
};
std::optional<GdbInfoStruct> GdbInfo;
int StatusCode {};
struct CallRetStackInfo {
uint64_t AllocationBase;
uint64_t AllocationEnd;
uint64_t DefaultLocation;
};
CallRetStackInfo GetCallRetStackInfo() {
uint64_t Base = reinterpret_cast<uint64_t>(Thread->CallRetStackBase);
// Leave some room from the base for the default location to allow for underflows without constant exceptions
return {Base - FEXCore::Utils::FEX_PAGE_SIZE, Base + FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE + FEXCore::Utils::FEX_PAGE_SIZE,
Base + FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE / 4};
}
// GDT and LDT tracking
FEXCore::Core::CPUState::gdt_segment gdt[32] {};
size_t ldt_entry_count {};
FEXCore::Core::CPUState::gdt_segment* ldt_entries {};
// 32-bit FD cache for DRM handlers.
fextl::unique_ptr<x32::DRMLRUCacheFDCache> DRMLRUCache {};
};
class ThreadManager final {
public:
ThreadManager(FEXCore::Context::Context* CTX, FEX::HLE::SignalDelegator* SignalDelegation)
: CTX {CTX}
, SignalDelegation {SignalDelegation} {}
~ThreadManager();
class StatAlloc final : public FEX::SHMStats::StatAllocBase {
public:
StatAlloc();
void LockBeforeFork();
void UnlockAfterFork(FEXCore::Core::InternalThreadState* Thread, bool Child);
void CleanupForExit();
FEXCore::SHMStats::ThreadStats* AllocateSlot(uint32_t TID);
void DeallocateSlot(FEXCore::SHMStats::ThreadStats* AllocatedSlot);
private:
void Initialize();
uint32_t FrontendAllocateSlots(uint32_t NewSize) override;
FEX_CONFIG_OPT(ProfileStats, PROFILESTATS);
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
constexpr static int USER_PERMS = S_IRWXU | S_IRWXG | S_IRWXO;
FEXCore::ForkableUniqueMutex StatMutex;
};
void CleanupForExit() {
Stat.CleanupForExit();
}
/**
* @brief Sets the calling thread's signal mask to the one provided
*
* @param Mask The new 64-bit signal mask to set
*
* @return The previous signal mask
*/
static uint64_t SetSignalMask(uint64_t Mask);
static void SetThreadName(const char* name);
///< Returns the ThreadStateObject from a CpuStateFrame object.
static inline FEX::HLE::ThreadStateObject* GetStateObjectFromCPUState(FEXCore::Core::CpuStateFrame* Frame) {
return static_cast<FEX::HLE::ThreadStateObject*>(Frame->Thread->FrontendPtr);
}
static inline FEX::HLE::ThreadStateObject* GetStateObjectFromFEXCoreThread(FEXCore::Core::InternalThreadState* Thread) {
return static_cast<FEX::HLE::ThreadStateObject*>(Thread->FrontendPtr);
}
FEX::HLE::ThreadStateObject* CreateThread(uint64_t InitialRIP, uint64_t StackPointer, const FEXCore::Core::CPUState* NewThreadState = nullptr,
uint64_t ParentTID = 0, FEX::HLE::ThreadStateObject* InheritThread = nullptr);
void TrackThread(FEX::HLE::ThreadStateObject* Thread) {
std::lock_guard lk(ThreadCreationMutex);
Threads.emplace_back(Thread);
}
void DestroyThread(FEX::HLE::ThreadStateObject* Thread, bool NeedsTLSUninstall = false);
void StopThread(FEX::HLE::ThreadStateObject* Thread);
void UnpauseThread(FEX::HLE::ThreadStateObject* Thread);
void Pause();
void Run();
void Step();
void Stop(bool IgnoreCurrentThread = false);
void WaitForIdle();
void WaitForIdleWithTimeout();
void WaitForThreadsToRun();
void SleepThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame);
void LockBeforeFork();
void UnlockAfterFork(FEXCore::Core::InternalThreadState* Thread, bool Child);
void IncrementIdleRefCount() {
++IdleWaitRefCount;
}
void InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState* CallingThread, uint64_t Start, uint64_t Length) {
std::lock_guard lk(ThreadCreationMutex);
// Potential deferred since Thread might not be valid.
// Thread object isn't valid very early in frontend's initialization.
// To be more optimal the frontend should provide this code with a valid Thread object earlier.
auto CodeInvalidationlk = FEXCore::GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), CallingThread);
CTX->InvalidateCodeBuffersCodeRange(Start, Length);
for (auto& Thread : Threads) {
CTX->InvalidateThreadCachedCodeRange(Thread->Thread, Start, Length);
}
}
void InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState* CallingThread, uint64_t Start, uint64_t Length,
FEXCore::Context::CodeRangeInvalidationFn after_callback) {
std::lock_guard lk(ThreadCreationMutex);
// Potential deferred since Thread might not be valid.
// Thread object isn't valid very early in frontend's initialization.
// To be more optimal the frontend should provide this code with a valid Thread object earlier.
auto CodeInvalidationlk = FEXCore::GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), CallingThread);
CTX->InvalidateCodeBuffersCodeRange(Start, Length);
for (auto& Thread : Threads) {
CTX->InvalidateThreadCachedCodeRange(Thread->Thread, Start, Length);
}
// Callback while holding the locks.
after_callback(Start, Length);
}
const fextl::vector<FEX::HLE::ThreadStateObject*>* GetThreads() const {
return &Threads;
}
private:
StatAlloc Stat;
FEXCore::Context::Context* CTX;
FEX::HLE::SignalDelegator* SignalDelegation;
FEXCore::ForkableUniqueMutex ThreadCreationMutex;
fextl::vector<FEX::HLE::ThreadStateObject*> Threads;
// Thread idling support.
bool Running {};
std::mutex IdleWaitMutex;
std::condition_variable IdleWaitCV;
std::atomic<uint32_t> IdleWaitRefCount {};
void HandleThreadDeletion(FEX::HLE::ThreadStateObject* Thread, bool NeedsTLSUninstall = false);
void NotifyPause();
FEX_CONFIG_OPT(ProfileStats, PROFILESTATS);
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
};
} // namespace FEX::HLE