Etterna 0.74.4
Loading...
Searching...
No Matches
RageThreads.h
1#ifndef RAGE_THREADS_H
2#define RAGE_THREADS_H
3
4#include "Etterna/Globals/global.h"
5#include "Etterna/Singletons/PrefsManager.h"
6
7#include <mutex>
8#include <atomic>
9#include <thread>
10#include <chrono>
11#include <functional>
12#include <condition_variable>
13
15{
16 public:
17 void waitForUpdate()
18 {
19 std::unique_lock<std::mutex> lk(_updatedMutex);
20 _updatedCV.wait_for(lk, std::chrono::milliseconds(100), [this] {
21 return this->getUpdated();
22 });
23 }
24 void setUpdated(bool b)
25 {
26 {
27 std::lock_guard<std::mutex> lk(_updatedMutex);
28 _updated = b;
29 }
30 _updatedCV.notify_all();
31 }
32 auto getUpdated() -> bool { return _updated; }
33 std::atomic<int> _threadsFinished{ 0 };
34 std::atomic<int> _progress{ 0 };
35 std::mutex _updatedMutex;
36 std::condition_variable _updatedCV;
37 void* data{ nullptr };
38
39 private:
40 std::atomic<bool> _updated{ false };
41};
42
43template<class T>
44using vectorIt = typename std::vector<T>::iterator;
45template<class T>
46using vectorRange = std::pair<vectorIt<T>, vectorIt<T>>;
47
48template<typename T>
49auto
50splitWorkLoad(std::vector<T>& v, size_t elementsPerThread)
51 -> std::vector<vectorRange<T>>
52{
53 std::vector<vectorRange<T>> ranges;
54 if (elementsPerThread <= 0 || elementsPerThread >= v.size()) {
55 ranges.push_back(std::make_pair(v.begin(), v.end()));
56 return ranges;
57 }
58
59 size_t range_count = (v.size() + 1) / elementsPerThread + 1;
60 size_t ePT = v.size() / range_count;
61 if (ePT == 0) {
62 ranges.push_back(std::make_pair(v.begin(), v.end()));
63 return ranges;
64 }
65 size_t i;
66
67 vectorIt<T> b = v.begin();
68
69 for (i = 0; i < v.size() - ePT; i += ePT)
70 ranges.push_back(std::make_pair(b + i, b + i + ePT));
71
72 ranges.push_back(std::make_pair(b + i, v.end()));
73 return ranges;
74}
75
76template<typename T>
77void
78parallelExecution(std::vector<T> vec,
79 std::function<void(int)> update,
80 std::function<void(vectorRange<T>, ThreadData*)> exec,
81 void* stuff)
82{
83 const int THREADS =
84 PREFSMAN->ThreadsToUse <= 0
85 ? std::thread::hardware_concurrency()
86 : PREFSMAN->ThreadsToUse <
87 static_cast<int>(std::thread::hardware_concurrency())
88 ? PREFSMAN->ThreadsToUse
89 : static_cast<int>(std::thread::hardware_concurrency());
90 std::vector<vectorRange<T>> workloads =
91 splitWorkLoad(vec, static_cast<size_t>(vec.size() / THREADS));
92 ThreadData data;
93 data.data = stuff;
94 auto threadCallback = [&data, &exec](vectorRange<T> workload) {
95 exec(workload, &data);
96 data._threadsFinished++;
97 data.setUpdated(true);
98 };
99 std::vector<std::thread> threadpool;
100 for (auto& workload : workloads)
101 threadpool.emplace_back(std::thread(threadCallback, workload));
102 while (data._threadsFinished < (int)workloads.size()) {
103 data.waitForUpdate();
104 update(data._progress);
105 data.setUpdated(false);
106 }
107 for (auto& thread : threadpool)
108 thread.join();
109}
110template<typename T>
111void
112parallelExecution(std::vector<T> vec,
113 std::function<void(int)> update,
114 std::function<void(vectorRange<T>, ThreadData)> exec)
115{
116 parallelExecution(vec, update, exec, nullptr);
117}
118
119template<typename T>
120void
121parallelExecution(std::vector<T> vec,
122 std::function<void(vectorRange<T>, ThreadData*)> exec)
123{
124 const int THREADS =
125 PREFSMAN->ThreadsToUse <= 0
126 ? std::thread::hardware_concurrency()
127 : PREFSMAN->ThreadsToUse <
128 static_cast<int>(std::thread::hardware_concurrency())
129 ? PREFSMAN->ThreadsToUse
130 : static_cast<int>(std::thread::hardware_concurrency());
131 std::vector<vectorRange<T>> workloads =
132 splitWorkLoad(vec, static_cast<size_t>(vec.size() / THREADS));
133 ThreadData data;
134 auto threadCallback = [&data, &exec](vectorRange<T> workload) {
135 exec(workload, &data);
136 data._threadsFinished++;
137 data.setUpdated(true);
138 };
139 std::vector<std::thread> threadpool;
140 for (auto& workload : workloads)
141 threadpool.emplace_back(std::thread(threadCallback, workload));
142 while (data._threadsFinished < (int)workloads.size()) {
143 data.waitForUpdate();
144 data.setUpdated(false);
145 }
146 for (auto& thread : threadpool)
147 thread.join();
148}
149
150struct ThreadSlot;
151class RageTimer;
152
155{
156 public:
157 RageThread();
158 RageThread(const RageThread& cpy);
159 ~RageThread();
160
161 void SetName(const std::string& n) { m_sName = n; }
162 [[nodiscard]] auto GetName() const -> std::string { return m_sName; }
163 void Create(int (*fn)(void*), void* data);
164
165 void Halt(bool Kill = false);
166 void Resume();
167
168 /* For crash handlers: kill or suspend all threads (except for
169 * the running one) immediately. */
170 static void HaltAllThreads(bool Kill = false);
171
172 /* If HaltAllThreads was called (with Kill==false), resume. */
173 static void ResumeAllThreads();
174
175 static auto GetCurrentThreadID() -> uint64_t;
176
177 static auto GetCurrentThreadName() -> const char*;
178 static auto GetThreadNameByID(uint64_t iID) -> const char*;
179 static auto EnumThreadIDs(int n, uint64_t& iID) -> bool;
180 auto Wait() -> int;
181 [[nodiscard]] auto IsCreated() const -> bool { return m_pSlot != nullptr; }
182
183 /* A system can define HAVE_TLS, indicating that it can compile thread_local
184 * code, but an individual environment may not actually have functional TLS.
185 * If this returns false, thread_local variables are considered undefined.
186 */
187 static auto GetSupportsTLS() -> bool { return s_bSystemSupportsTLS; }
188 static void SetSupportsTLS(bool b) { s_bSystemSupportsTLS = b; }
189
190 static auto GetIsShowingDialog() -> bool { return s_bIsShowingDialog; }
191 static void SetIsShowingDialog(bool b) { s_bIsShowingDialog = b; }
192 static auto GetInvalidThreadID() -> uint64_t;
193
194 private:
195 ThreadSlot* m_pSlot;
196 std::string m_sName;
197
198 static bool s_bSystemSupportsTLS;
199 static bool s_bIsShowingDialog;
200
201 // Swallow up warnings. If they must be used, define them.
202 auto operator=(const RageThread& rhs) -> RageThread&;
203};
204
211{
212 public:
213 RageThreadRegister(const std::string& sName);
215
216 private:
217 ThreadSlot* m_pSlot;
218 // Swallow up warnings. If they must be used, define them.
219 auto operator=(const RageThreadRegister& rhs)
220 -> RageThreadRegister& = delete;
221 RageThreadRegister(const RageThreadRegister& rhs) = delete;
222};
223
224
225/* Mutex class that follows the behavior of Windows mutexes: if the same
226 * thread locks the same mutex twice, we just increase a refcount; a mutex
227 * is considered unlocked when the refcount reaches zero. This is more
228 * convenient, though much slower on some archs. (We don't have any tightly-
229 * coupled threads, so that's OK.) */
230class MutexImpl;
232{
233 public:
234 [[nodiscard]] auto GetName() const -> std::string { return m_sName; }
235 void SetName(const std::string& s) { m_sName = s; }
236 virtual void Lock();
237 virtual auto TryLock() -> bool;
238 virtual void Unlock();
239 [[nodiscard]] virtual auto IsLockedByThisThread() const -> bool;
240
241 RageMutex(const std::string& name);
242 virtual ~RageMutex();
243
244 protected:
245 MutexImpl* m_pMutex;
246 std::string m_sName;
247
248 uint64_t m_LockedBy;
249 int m_LockCnt;
250
251 void MarkLockedMutex();
252
253 private:
254 // Swallow up warnings. If they must be used, define them.
255 auto operator=(const RageMutex& rhs) -> RageMutex&;
256 RageMutex(const RageMutex& rhs);
257};
258
264{
265 RageMutex& mutex;
266
267 const char* file;
268 int line;
269 float locked_at;
270 bool locked;
271
272 public:
273 LockMutex(RageMutex& mut, const char* file, int line);
274 LockMutex(RageMutex& mut)
275 : mutex(mut)
276 , file(nullptr)
277 , line(-1)
278 , locked_at(-1)
279 , locked(true)
280 {
281 mutex.Lock();
282 }
283 ~LockMutex();
284 LockMutex(LockMutex& cpy)
285 : mutex(cpy.mutex)
286 , file(nullptr)
287 , line(-1)
288 , locked_at(cpy.locked_at)
289 , locked(true)
290 {
291 mutex.Lock();
292 }
293
298 void Unlock();
299
300 private:
301 // Swallow up warnings. If they must be used, define them.
302 auto operator=(const LockMutex& rhs) -> LockMutex& = delete;
303};
304
305#define LockMut(m) LockMutex SM_UNIQUE_NAME(LocalLock)(m, __FILE__, __LINE__)
306
307class EventImpl;
308class RageEvent : public RageMutex
309{
310 public:
311 RageEvent(const std::string& name);
312 ~RageEvent() override;
313
314 /*
315 * If pTimeout is non-NULL, the event will be automatically signalled at the
316 * given time. Note that implementing this timeout is optional; not all
317 * archs support it. If false is returned, the wait timed out (and the mutex
318 * is locked, as if the event had been signalled).
319 */
320 auto Wait(float timeout = 0.F) -> bool;
321 void Signal();
322 void Broadcast();
323 [[nodiscard]] auto WaitTimeoutSupported() const -> bool;
324 // Swallow up warnings. If they must be used, define them.
325 auto operator=(const RageEvent& rhs) -> RageEvent&;
326 RageEvent(const RageEvent& rhs);
327
328 private:
329 EventImpl* m_pEvent;
330};
331
332class SemaImpl;
334{
335 public:
336 RageSemaphore(const std::string& sName, int iInitialValue = 0);
338
339 [[nodiscard]] auto GetName() const -> std::string { return m_sName; }
340 [[nodiscard]] auto GetValue() const -> int;
341 void Post();
342 void Wait(bool bFailOnTimeout = true);
343 auto TryWait() -> bool;
344
345 private:
346 SemaImpl* m_pSema;
347 std::string m_sName;
348
349 // Swallow up warnings. If they must be used, define them.
350 auto operator=(const RageSemaphore& rhs) -> RageSemaphore& = delete;
351 RageSemaphore(const RageSemaphore& rhs) = delete;
352};
353
354#endif
Definition Threads.h:55
Lock a mutex on construction, unlock it on destruction.
Definition RageThreads.h:264
void Unlock()
Unlock the mutex (before this would normally go out of scope).
Definition RageThreads.cpp:526
Definition Threads.h:25
Definition RageThreads.h:309
Definition RageThreads.h:232
Definition RageThreads.h:334
Register a thread created outside of RageThread.
Definition RageThreads.h:211
Thread, mutex, semaphore, and event classes.
Definition RageThreads.h:155
Definition RageTimer.h:9
Definition Threads.h:65
Definition RageThreads.h:15
Definition RageThreads.cpp:39