OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
workers.cpp
Go to the documentation of this file.
1#include "workers.h"
2#include "utils/string_frm.h"
3
4#include <Tempest/Platform>
5#include <Tempest/Log>
6
7#if defined(__WINDOWS__)
8#include <windows.h>
9#include <processthreadsapi.h>
10#endif
11
12#if defined(__GNUC__)
13#include <pthread.h>
14#endif
15
16#if defined(_MSC_VER)
17void Workers::setThreadName(const char* threadName) {
18 const DWORD MS_VC_EXCEPTION = 0x406D1388;
19 DWORD dwThreadID = GetCurrentThreadId();
20#pragma pack(push,8)
21 struct THREADNAME_INFO {
22 DWORD dwType = 0x1000; // Must be 0x1000.
23 LPCSTR szName; // Pointer to name (in user addr space).
24 DWORD dwThreadID; // Thread ID (-1=caller thread).
25 DWORD dwFlags; // Reserved for future use, must be zero.
26 };
27#pragma pack(pop)
28
29 THREADNAME_INFO info = {};
30 info.szName = threadName;
31 info.dwThreadID = dwThreadID;
32 info.dwFlags = 0;
33
34 __try {
35 RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
36 }
37 __except(EXCEPTION_EXECUTE_HANDLER) {
38 }
39 }
40#elif defined(__WINDOWS__)
41void Workers::setThreadName(const char* threadName) {
42#if defined(__GNUC__)
43 pthread_setname_np(pthread_self(), threadName);
44#endif
45 auto k32 = GetModuleHandleA("Kernel32");
46 auto fn = GetProcAddress(k32, "SetThreadDescription");
47 if(fn==nullptr)
48 return;
49
50 // nsight does not care about pthread_setname_np
51 WCHAR wname[64] = {};
52 for(size_t i=0; i<63 && threadName[i]; ++i)
53 wname[i] = WCHAR(threadName[i]);
54 auto SetThreadDescription = reinterpret_cast<HRESULT(WINAPI*)(HANDLE,PCWSTR)>(fn);
55 SetThreadDescription(GetCurrentThread(), wname);
56 }
57#elif defined(__GNUC__) && !defined(__clang__)
58void Workers::setThreadName(const char* threadName){
59 pthread_setname_np(pthread_self(), threadName);
60 }
61#elif defined(__OSX__)
62void Workers::setThreadName(const char* threadName){
63 pthread_setname_np(threadName);
64 }
65#else
66void Workers::setThreadName(const char* threadName) { (void)threadName; }
67#endif
68
69using namespace Tempest;
70
71const size_t Workers::taskPerThread = 128;
72const size_t Workers::taskPerStep = 16;
73
75 size_t id=0;
76 for(auto& i:th) {
77 i = std::thread([this,id]() noexcept {
78 threadFunc(id);
79 });
80 ++id;
81 }
82 }
83
85 running = false;
86 workSet = nullptr;
87 workSize = MAX_THREADS;
88 execWork(minWorkSize<void,void>());
89 for(auto& i:th)
90 i.join();
91 }
92
93Workers &Workers::inst() {
94 static Workers w;
95 return w;
96 }
97
99 int32_t th = int32_t(std::thread::hardware_concurrency());
100 if(th<=0)
101 th = 1;
102 if(th>MAX_THREADS)
103 return MAX_THREADS;
104 return uint8_t(th);
105 }
106
107void Workers::threadFunc(size_t id) {
108 {
109 string_frm tname("Workers [",int(id),"]");
110 setThreadName(tname.c_str());
111 }
112
113 while(true) {
114 {
115 std::unique_lock<std::mutex> lck(sync);
116 workWait.wait(lck, [this]() { return workTbd>0; });
117 --workTbd;
118 }
119
120 if(!running) {
121 taskDone.fetch_add(1);
122 return;
123 }
124
125 if(workSet==nullptr) {
126 auto idx = progressIt.fetch_add(1);
127 workFunc(workSet+idx, 1);
128 } else {
129 taskLoop();
130 }
131
132 taskDone.fetch_add(1);
133 // if(size_t(taskDone.fetch_add(1)+1)==taskCount)
134 // std::this_thread::yield();
135 }
136 }
137
138uint32_t Workers::taskLoop() {
139 uint32_t count = 0;
140 while(true) {
141 size_t b = size_t(progressIt.fetch_add(taskPerStep));
142 size_t e = std::min(b+taskPerStep, workSize);
143 if(e<=b)
144 break;
145
146 void* d = workSet + b*workEltSize;
147 workFunc(d,e-b);
148 count += uint32_t(e-b);
149 }
150 return count;
151 }
152
153void Workers::execWork(uint32_t& minElts) {
154 if(workSize==0)
155 return;
156
157 if(workSet!=nullptr) {
158 const auto maxTheads = maxThreads();
159 taskCount = uint32_t((workSize+taskPerThread-1)/taskPerThread);
160 taskCount--; // main thread also do tasks
161 if(taskCount>maxTheads)
162 taskCount = maxTheads;
163 if(taskCount<=0)
164 taskCount = 1;
165 } else {
166 taskCount = uint32_t(workSize);
167 }
168
169 if(running && taskCount==1) {
170 workFunc(workSet, workSize);
171 return;
172 }
173
174 minElts = std::max<uint32_t>(minElts, taskPerThread);
175
176 if(workSet!=nullptr && workSize<=minElts && true) {
177 workFunc(workSet, workSize);
178 if(minElts > workSize*2)
179 minElts = 0;
180 return;
181 }
182
183 progressIt.store(0);
184 taskDone.store(0);
185
186 {
187 std::unique_lock<std::mutex> lck(sync);
188 workTbd = int32_t(taskCount);
189 }
190 workWait.notify_all();
191
192 uint32_t cnt = 0;
193 if(workSet==nullptr) {
194 std::this_thread::yield();
195 } else {
196 cnt = taskLoop(); (void)cnt;
197 }
198
199 while(true) {
200 int expect = int(taskCount);
201 if(taskDone.load()==expect) {
202 taskDone.store(0);
203 break;
204 }
205 std::this_thread::yield();
206 }
207 }
~Workers()
Definition workers.cpp:84
Workers()
Definition workers.cpp:74
static uint8_t maxThreads()
Definition workers.cpp:98
static void setThreadName(const char *threadName)
Definition workers.cpp:66