1// [Blend2D]
2// 2D Vector Graphics Powered by a JIT Compiler.
3//
4// [License]
5// Zlib - See LICENSE.md file in the package.
6
7#include "./blapi-build_p.h"
8#include "./blruntime_p.h"
9#include "./blsupport_p.h"
10#include "./blthreading_p.h"
11
12#ifdef _WIN32
13 #include <process.h>
14#endif
15
16// ============================================================================
17// [Globals]
18// ============================================================================
19
20static BLThreadVirt blThreadVirt;
21
22// ============================================================================
23// [BLThreadEvent - Windows]
24// ============================================================================
25
26#ifdef _WIN32
27BLResult blThreadEventCreate(BLThreadEvent* self, bool manualReset, bool signaled) noexcept {
28 HANDLE h = CreateEventW(nullptr, manualReset, signaled, nullptr);
29 if (BL_UNLIKELY(!h)) {
30 self->handle = -1;
31 return blTraceError(blResultFromWinError(GetLastError()));
32 }
33
34 self->handle = (intptr_t)h;
35 return BL_SUCCESS;
36}
37
38BLResult blThreadEventDestroy(BLThreadEvent* self) noexcept {
39 if (BL_UNLIKELY(self->handle == -1))
40 return blTraceError(BL_ERROR_INVALID_HANDLE);
41
42 CloseHandle((HANDLE)self->handle);
43 self->handle = -1;
44 return BL_SUCCESS;
45}
46
47bool blThreadEventIsSignaled(const BLThreadEvent* self) noexcept {
48 if (BL_UNLIKELY(self->handle == -1))
49 return false;
50 return WaitForSingleObject((HANDLE)self->handle, 0) == WAIT_OBJECT_0;
51}
52
53BLResult blThreadEventSignal(BLThreadEvent* self) noexcept {
54 if (BL_UNLIKELY(self->handle == -1))
55 return blTraceError(BL_ERROR_INVALID_HANDLE);
56
57 SetEvent((HANDLE)self->handle);
58 return BL_SUCCESS;
59}
60
61BLResult blThreadEventReset(BLThreadEvent* self) noexcept {
62 if (BL_UNLIKELY(self->handle == -1))
63 return blTraceError(BL_ERROR_INVALID_HANDLE);
64
65 ResetEvent((HANDLE)self->handle);
66 return BL_SUCCESS;
67}
68
69BLResult blThreadEventWaitInternal(BLThreadEvent* self, DWORD dwMilliseconds) noexcept {
70 if (BL_UNLIKELY(self->handle == -1))
71 return blTraceError(BL_ERROR_INVALID_HANDLE);
72
73 DWORD result = WaitForSingleObject((HANDLE)self->handle, dwMilliseconds);
74 switch (result) {
75 case WAIT_OBJECT_0:
76 return BL_SUCCESS;
77
78 case WAIT_FAILED:
79 return blTraceError(blResultFromWinError(GetLastError()));
80
81 case WAIT_TIMEOUT:
82 return blTraceError(BL_ERROR_TIMED_OUT);
83
84 default:
85 return blTraceError(BL_ERROR_INVALID_STATE);
86 };
87}
88
89BLResult blThreadEventWait(BLThreadEvent* self) noexcept {
90 return blThreadEventWaitInternal(self, INFINITE);
91}
92
93BLResult blThreadEventTimedWait(BLThreadEvent* self, uint64_t microseconds) noexcept {
94 uint32_t milliseconds = uint32_t(blMin<uint64_t>(microseconds / 1000u, INFINITE));
95 return blThreadEventWaitInternal(self, milliseconds);
96}
97#endif
98
99// ============================================================================
100// [BLThreadEvent - Posix]
101// ============================================================================
102
103#ifndef _WIN32
104struct BLThreadEventPosixImpl {
105 BLConditionVariable cond;
106 BLMutex mutex;
107 uint32_t manualReset;
108 uint32_t signaled;
109
110 BL_INLINE BLThreadEventPosixImpl(bool manualReset, bool signaled) noexcept
111 : cond(),
112 mutex(),
113 manualReset(manualReset),
114 signaled(signaled) {}
115};
116
117BLResult blThreadEventCreate(BLThreadEvent* self, bool manualReset, bool signaled) noexcept {
118 self->handle = -1;
119 void* p = malloc(sizeof(BLThreadEventPosixImpl));
120
121 if (!p)
122 return BL_ERROR_OUT_OF_MEMORY;
123
124 BLThreadEventPosixImpl* impl = new(p) BLThreadEventPosixImpl(manualReset, signaled);
125 self->handle = (intptr_t)impl;
126 return BL_SUCCESS;
127}
128
129BLResult blThreadEventDestroy(BLThreadEvent* self) noexcept {
130 if (self->handle == -1)
131 return blTraceError(BL_ERROR_INVALID_HANDLE);
132
133 BLThreadEventPosixImpl* impl = (BLThreadEventPosixImpl*)self->handle;
134 impl->~BLThreadEventPosixImpl();
135 free(impl);
136
137 self->handle = -1;
138 return BL_SUCCESS;
139}
140
141bool blThreadEventIsSignaled(const BLThreadEvent* self) noexcept {
142 if (self->handle == -1)
143 return false;
144
145 BLThreadEventPosixImpl* impl = (BLThreadEventPosixImpl*)self->handle;
146 BLMutexGuard guard(impl->mutex);
147
148 return impl->signaled != 0;
149}
150
151BLResult blThreadEventSignal(BLThreadEvent* self) noexcept {
152 if (self->handle == -1)
153 return blTraceError(BL_ERROR_INVALID_HANDLE);
154
155 BLThreadEventPosixImpl* impl = (BLThreadEventPosixImpl*)self->handle;
156 BLMutexGuard guard(impl->mutex);
157
158 if (!impl->signaled) {
159 impl->signaled = true;
160 if (impl->manualReset)
161 impl->cond.broadcast();
162 else
163 impl->cond.signal();
164 }
165
166 return BL_SUCCESS;
167}
168
169BLResult blThreadEventReset(BLThreadEvent* self) noexcept {
170 if (self->handle == -1)
171 return blTraceError(BL_ERROR_INVALID_HANDLE);
172
173 BLThreadEventPosixImpl* impl = (BLThreadEventPosixImpl*)self->handle;
174 BLMutexGuard guard(impl->mutex);
175
176 impl->signaled = false;
177 return BL_SUCCESS;
178}
179
180BLResult blThreadEventWait(BLThreadEvent* self) noexcept {
181 if (self->handle == -1)
182 return blTraceError(BL_ERROR_INVALID_HANDLE);
183
184 BLThreadEventPosixImpl* impl = (BLThreadEventPosixImpl*)self->handle;
185 BLMutexGuard guard(impl->mutex);
186
187 while (!impl->signaled)
188 impl->cond.wait(impl->mutex);
189
190 if (!impl->manualReset)
191 impl->signaled = false;
192
193 return BL_SUCCESS;
194}
195
196BLResult blThreadEventTimedWait(BLThreadEvent* self, uint64_t microseconds) noexcept {
197 if (self->handle == -1)
198 return blTraceError(BL_ERROR_INVALID_HANDLE);
199
200 struct timespec absTime;
201 blGetAbsTimeForWaitCondition(absTime, microseconds);
202
203 BLThreadEventPosixImpl* impl = (BLThreadEventPosixImpl*)self->handle;
204 BLMutexGuard guard(impl->mutex);
205
206 while (!impl->signaled)
207 if (impl->cond.timedWait(impl->mutex, &absTime) == BL_ERROR_TIMED_OUT)
208 return BL_ERROR_TIMED_OUT;
209
210 if (!impl->manualReset)
211 impl->signaled = false;
212
213 return BL_SUCCESS;
214}
215#endif
216
217// ============================================================================
218// [BLThread - Internal]
219// ============================================================================
220
221class BLInternalThread : public BLThread {
222public:
223#ifdef _WIN32
224 intptr_t handle;
225#else
226 pthread_t handle;
227#endif
228
229 BLThreadEvent event;
230 volatile uint32_t internalStatus;
231 volatile uint32_t reserved;
232
233 BLThreadFunc workFunc;
234 BLThreadFunc doneFunc;
235 void* workData;
236
237 BLThreadFunc exitFunc;
238 void* exitData;
239
240 BL_INLINE BLInternalThread(BLThreadFunc exitFunc, void* exitData) noexcept
241 : BLThread { &blThreadVirt },
242 handle {},
243 event(true, false),
244 internalStatus(BL_THREAD_STATUS_IDLE),
245 reserved(0),
246 workFunc(nullptr),
247 doneFunc(nullptr),
248 workData(nullptr),
249 exitFunc(exitFunc),
250 exitData(exitData) {}
251
252 BL_INLINE ~BLInternalThread() noexcept {
253#if _WIN32
254 // The handle MUST be closed.
255 if (handle != 0)
256 CloseHandle((HANDLE)handle);
257#endif
258 }
259};
260
261static BLInternalThread* blThreadNew(BLThreadFunc exitFunc, void* exitData) noexcept {
262 BLInternalThread* self = static_cast<BLInternalThread*>(malloc(sizeof(BLInternalThread)));
263 if (BL_UNLIKELY(!self))
264 return nullptr;
265 return new(self) BLInternalThread(exitFunc, exitData);
266}
267
268static BLResult BL_CDECL blThreadDestroy(BLThread* self_) noexcept {
269 BLInternalThread* self = static_cast<BLInternalThread*>(self_);
270 BL_ASSERT(self != nullptr);
271
272 self->~BLInternalThread();
273 free(self);
274
275 return BL_SUCCESS;
276}
277
278static BL_INLINE void blThreadEntryPoint(BLInternalThread* thread) noexcept {
279 for (;;) {
280 // Wait for some work to do.
281 thread->event.wait();
282 blAtomicThreadFence(std::memory_order_acquire);
283
284 BLThreadFunc workFunc = thread->workFunc;
285 BLThreadFunc doneFunc = thread->doneFunc;
286 void* workData = thread->workData;
287
288 thread->workFunc = nullptr;
289 thread->doneFunc = nullptr;
290 thread->workData = nullptr;
291
292 // If the compare-exchange fails and the function was not provided it means that this thread is quitting.
293 uint32_t value = BL_THREAD_STATUS_IDLE;
294 if (!std::atomic_compare_exchange_strong((std::atomic<uint32_t>*)&thread->internalStatus, &value, uint32_t(BL_THREAD_STATUS_RUNNING)) && !workFunc)
295 break;
296
297 // Reset the event - more work can be queued from now...
298 thread->event.reset();
299
300 // Run the task.
301 workFunc(thread, workData);
302
303 // Again, if the compare-exchange fails it means we are quitting.
304 value = BL_THREAD_STATUS_RUNNING;
305 bool res = !std::atomic_compare_exchange_strong((std::atomic<uint32_t>*)&thread->internalStatus, &value, uint32_t(BL_THREAD_STATUS_IDLE));
306
307 if (doneFunc)
308 doneFunc(thread, workData);
309
310 if (!res && value == BL_THREAD_STATUS_QUITTING)
311 break;
312 }
313
314 thread->exitFunc(thread, thread->exitData);
315}
316
317static uint32_t BL_CDECL blThreadStatus(const BLThread* self_) noexcept {
318 const BLInternalThread* self = static_cast<const BLInternalThread*>(self_);
319 return blAtomicFetch(&self->internalStatus);
320}
321
322static BLResult BL_CDECL blThreadRun(BLThread* self_, BLThreadFunc workFunc, BLThreadFunc doneFunc, void* data) noexcept {
323 BLInternalThread* self = static_cast<BLInternalThread*>(self_);
324 if (self->event.isSignaled())
325 return blTraceError(BL_ERROR_BUSY);
326
327 blAtomicThreadFence(std::memory_order_release);
328 self->workFunc = workFunc;
329 self->doneFunc = doneFunc;
330 self->workData = data;
331 self->event.signal();
332
333 return BL_SUCCESS;
334}
335
336static BLResult BL_CDECL blThreadQuit(BLThread* self_) noexcept {
337 BLInternalThread* self = static_cast<BLInternalThread*>(self_);
338
339 std::atomic_store((std::atomic<uint32_t>*)&self->internalStatus, uint32_t(BL_THREAD_STATUS_QUITTING));
340 self->event.signal();
341
342 return BL_SUCCESS;
343}
344
345// ============================================================================
346// [BLThread - Windows]
347// ============================================================================
348
349#ifdef _WIN32
350static unsigned BL_STDCALL blThreadEntryPointWrapper(void* arg) {
351 BLInternalThread* thread = static_cast<BLInternalThread*>(arg);
352 blThreadEntryPoint(thread);
353 return 0;
354}
355
356BLResult BL_CDECL blThreadCreate(BLThread** threadOut, const BLThreadAttributes* attributes, BLThreadFunc exitFunc, void* exitData) noexcept {
357 BLInternalThread* thread = blThreadNew(exitFunc, exitData);
358 if (BL_UNLIKELY(!thread))
359 return blTraceError(BL_ERROR_OUT_OF_MEMORY);
360
361 BLResult result = BL_SUCCESS;
362 if (!thread->event.isInitialized()) {
363 result = blTraceError(BL_ERROR_OUT_OF_MEMORY);
364 }
365 else {
366 uint32_t flags = 0;
367 uint32_t stackSize = attributes->stackSize;
368
369 if (stackSize > 0)
370 flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
371
372 HANDLE handle = (HANDLE)_beginthreadex(nullptr, stackSize, blThreadEntryPointWrapper, thread, flags, nullptr);
373 if (handle == (HANDLE)-1)
374 result = BL_ERROR_BUSY;
375 else
376 thread->handle = (intptr_t)handle;
377 }
378
379 if (result == BL_SUCCESS) {
380 *threadOut = thread;
381 return BL_SUCCESS;
382 }
383 else {
384 thread->~BLInternalThread();
385 free(thread);
386
387 *threadOut = nullptr;
388 return result;
389 }
390}
391#endif
392
393// ============================================================================
394// [BLThread - Posix]
395// ============================================================================
396
397#ifndef _WIN32
398static void* blThreadEntryPointWrapper(void* arg) {
399 blThreadEntryPoint(static_cast<BLInternalThread*>(arg));
400 return nullptr;
401}
402
403BLResult BL_CDECL blThreadCreate(BLThread** threadOut, const BLThreadAttributes* attributes, BLThreadFunc exitFunc, void* exitData) noexcept {
404 pthread_attr_t ptAttr;
405 int err = pthread_attr_init(&ptAttr);
406
407 if (err)
408 return blResultFromPosixError(err);
409
410 BLResult result = blThreadSetPtAttributes(&ptAttr, attributes);
411 if (result == BL_SUCCESS)
412 result = blThreadCreatePt(threadOut, &ptAttr, exitFunc, exitData);
413
414 err = pthread_attr_destroy(&ptAttr);
415 BL_ASSERT(err == 0);
416 BL_UNUSED(err);
417
418 return result;
419}
420
421BLResult blThreadCreatePt(BLThread** threadOut, const pthread_attr_t* ptAttr, BLThreadFunc exitFunc, void* exitData) noexcept {
422 BLInternalThread* thread = blThreadNew(exitFunc, exitData);
423 if (BL_UNLIKELY(!thread))
424 return blTraceError(BL_ERROR_OUT_OF_MEMORY);
425
426 int err;
427 if (!thread->event.isInitialized())
428 err = ENOMEM;
429 else
430 err = pthread_create(&thread->handle, ptAttr, blThreadEntryPointWrapper, thread);
431
432 if (!err) {
433 *threadOut = thread;
434 return BL_SUCCESS;
435 }
436 else {
437 thread->~BLInternalThread();
438 free(thread);
439
440 *threadOut = nullptr;
441 return blResultFromPosixError(err);
442 }
443}
444
445BLResult blThreadSetPtAttributes(pthread_attr_t* ptAttr, const BLThreadAttributes* src) noexcept {
446 pthread_attr_setdetachstate(ptAttr, PTHREAD_CREATE_DETACHED);
447 if (src->stackSize) {
448 int err = pthread_attr_setstacksize(ptAttr, src->stackSize);
449 if (err)
450 return blResultFromPosixError(err);
451 }
452 return BL_SUCCESS;
453}
454#endif
455
456// ============================================================================
457// [BLThreading - RuntimeInit]
458// ============================================================================
459
460void blThreadingRtInit(BLRuntimeContext* rt) noexcept {
461 BL_UNUSED(rt);
462
463 // BLThread virtual table.
464 blThreadVirt.destroy = blThreadDestroy;
465 blThreadVirt.status = blThreadStatus;
466 blThreadVirt.run = blThreadRun;
467 blThreadVirt.quit = blThreadQuit;
468}
469