1 /* Threads.c -- multithreading library
2 2023-03-04 : Igor Pavlov : Public domain */
3
4 #include "Precomp.h"
5
6 #ifdef _WIN32
7
8 #ifndef USE_THREADS_CreateThread
9 #include <process.h>
10 #endif
11
12 #include "Threads.h"
13
GetError(void)14 static WRes GetError(void)
15 {
16 const DWORD res = GetLastError();
17 return res ? (WRes)res : 1;
18 }
19
HandleToWRes(HANDLE h)20 static WRes HandleToWRes(HANDLE h) { return (h != NULL) ? 0 : GetError(); }
BOOLToWRes(BOOL v)21 static WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); }
22
HandlePtr_Close(HANDLE * p)23 WRes HandlePtr_Close(HANDLE *p)
24 {
25 if (*p != NULL)
26 {
27 if (!CloseHandle(*p))
28 return GetError();
29 *p = NULL;
30 }
31 return 0;
32 }
33
Handle_WaitObject(HANDLE h)34 WRes Handle_WaitObject(HANDLE h)
35 {
36 DWORD dw = WaitForSingleObject(h, INFINITE);
37 /*
38 (dw) result:
39 WAIT_OBJECT_0 // 0
40 WAIT_ABANDONED // 0x00000080 : is not compatible with Win32 Error space
41 WAIT_TIMEOUT // 0x00000102 : is compatible with Win32 Error space
42 WAIT_FAILED // 0xFFFFFFFF
43 */
44 if (dw == WAIT_FAILED)
45 {
46 dw = GetLastError();
47 if (dw == 0)
48 return WAIT_FAILED;
49 }
50 return (WRes)dw;
51 }
52
53 #define Thread_Wait(p) Handle_WaitObject(*(p))
54
Thread_Wait_Close(CThread * p)55 WRes Thread_Wait_Close(CThread *p)
56 {
57 WRes res = Thread_Wait(p);
58 WRes res2 = Thread_Close(p);
59 return (res != 0 ? res : res2);
60 }
61
Thread_Create(CThread * p,THREAD_FUNC_TYPE func,LPVOID param)62 WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
63 {
64 /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
65
66 #ifdef USE_THREADS_CreateThread
67
68 DWORD threadId;
69 *p = CreateThread(NULL, 0, func, param, 0, &threadId);
70
71 #else
72
73 unsigned threadId;
74 *p = (HANDLE)(_beginthreadex(NULL, 0, func, param, 0, &threadId));
75
76 #endif
77
78 /* maybe we must use errno here, but probably GetLastError() is also OK. */
79 return HandleToWRes(*p);
80 }
81
82
Thread_Create_With_Affinity(CThread * p,THREAD_FUNC_TYPE func,LPVOID param,CAffinityMask affinity)83 WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, CAffinityMask affinity)
84 {
85 #ifdef USE_THREADS_CreateThread
86
87 UNUSED_VAR(affinity)
88 return Thread_Create(p, func, param);
89
90 #else
91
92 /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
93 HANDLE h;
94 WRes wres;
95 unsigned threadId;
96 h = (HANDLE)(_beginthreadex(NULL, 0, func, param, CREATE_SUSPENDED, &threadId));
97 *p = h;
98 wres = HandleToWRes(h);
99 if (h)
100 {
101 {
102 // DWORD_PTR prevMask =
103 SetThreadAffinityMask(h, (DWORD_PTR)affinity);
104 /*
105 if (prevMask == 0)
106 {
107 // affinity change is non-critical error, so we can ignore it
108 // wres = GetError();
109 }
110 */
111 }
112 {
113 DWORD prevSuspendCount = ResumeThread(h);
114 /* ResumeThread() returns:
115 0 : was_not_suspended
116 1 : was_resumed
117 -1 : error
118 */
119 if (prevSuspendCount == (DWORD)-1)
120 wres = GetError();
121 }
122 }
123
124 /* maybe we must use errno here, but probably GetLastError() is also OK. */
125 return wres;
126
127 #endif
128 }
129
130
Event_Create(CEvent * p,BOOL manualReset,int signaled)131 static WRes Event_Create(CEvent *p, BOOL manualReset, int signaled)
132 {
133 *p = CreateEvent(NULL, manualReset, (signaled ? TRUE : FALSE), NULL);
134 return HandleToWRes(*p);
135 }
136
Event_Set(CEvent * p)137 WRes Event_Set(CEvent *p) { return BOOLToWRes(SetEvent(*p)); }
Event_Reset(CEvent * p)138 WRes Event_Reset(CEvent *p) { return BOOLToWRes(ResetEvent(*p)); }
139
ManualResetEvent_Create(CManualResetEvent * p,int signaled)140 WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled) { return Event_Create(p, TRUE, signaled); }
AutoResetEvent_Create(CAutoResetEvent * p,int signaled)141 WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled) { return Event_Create(p, FALSE, signaled); }
ManualResetEvent_CreateNotSignaled(CManualResetEvent * p)142 WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) { return ManualResetEvent_Create(p, 0); }
AutoResetEvent_CreateNotSignaled(CAutoResetEvent * p)143 WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) { return AutoResetEvent_Create(p, 0); }
144
145
Semaphore_Create(CSemaphore * p,UInt32 initCount,UInt32 maxCount)146 WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
147 {
148 // negative ((LONG)maxCount) is not supported in WIN32::CreateSemaphore()
149 *p = CreateSemaphore(NULL, (LONG)initCount, (LONG)maxCount, NULL);
150 return HandleToWRes(*p);
151 }
152
Semaphore_OptCreateInit(CSemaphore * p,UInt32 initCount,UInt32 maxCount)153 WRes Semaphore_OptCreateInit(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
154 {
155 // if (Semaphore_IsCreated(p))
156 {
157 WRes wres = Semaphore_Close(p);
158 if (wres != 0)
159 return wres;
160 }
161 return Semaphore_Create(p, initCount, maxCount);
162 }
163
Semaphore_Release(CSemaphore * p,LONG releaseCount,LONG * previousCount)164 static WRes Semaphore_Release(CSemaphore *p, LONG releaseCount, LONG *previousCount)
165 { return BOOLToWRes(ReleaseSemaphore(*p, releaseCount, previousCount)); }
Semaphore_ReleaseN(CSemaphore * p,UInt32 num)166 WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num)
167 { return Semaphore_Release(p, (LONG)num, NULL); }
Semaphore_Release1(CSemaphore * p)168 WRes Semaphore_Release1(CSemaphore *p) { return Semaphore_ReleaseN(p, 1); }
169
CriticalSection_Init(CCriticalSection * p)170 WRes CriticalSection_Init(CCriticalSection *p)
171 {
172 /* InitializeCriticalSection() can raise exception:
173 Windows XP, 2003 : can raise a STATUS_NO_MEMORY exception
174 Windows Vista+ : no exceptions */
175 #ifdef _MSC_VER
176 #ifdef __clang__
177 #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
178 #endif
179 __try
180 #endif
181 {
182 InitializeCriticalSection(p);
183 /* InitializeCriticalSectionAndSpinCount(p, 0); */
184 }
185 #ifdef _MSC_VER
186 __except (EXCEPTION_EXECUTE_HANDLER) { return ERROR_NOT_ENOUGH_MEMORY; }
187 #endif
188 return 0;
189 }
190
191
192
193
194 #else // _WIN32
195
196 // ---------- POSIX ----------
197
198 #ifndef __APPLE__
199 #ifndef Z7_AFFINITY_DISABLE
200 // _GNU_SOURCE can be required for pthread_setaffinity_np() / CPU_ZERO / CPU_SET
201 // clang < 3.6 : unknown warning group '-Wreserved-id-macro'
202 // clang 3.6 - 12.01 : gives warning "macro name is a reserved identifier"
203 // clang >= 13 : do not give warning
204 #if !defined(_GNU_SOURCE)
205 #if defined(__clang__) && (__clang_major__ >= 4) && (__clang_major__ <= 12)
206 #pragma GCC diagnostic ignored "-Wreserved-id-macro"
207 #endif
208 #define _GNU_SOURCE
209 #endif // !defined(_GNU_SOURCE)
210 #endif // Z7_AFFINITY_DISABLE
211 #endif // __APPLE__
212
213 #include "Threads.h"
214
215 #include <errno.h>
216 #include <stdlib.h>
217 #include <string.h>
218 #ifdef Z7_AFFINITY_SUPPORTED
219 // #include <sched.h>
220 #endif
221
222
223 // #include <stdio.h>
224 // #define PRF(p) p
225 #define PRF(p)
226 #define Print(s) PRF(printf("\n%s\n", s);)
227
Thread_Create_With_CpuSet(CThread * p,THREAD_FUNC_TYPE func,LPVOID param,const CCpuSet * cpuSet)228 WRes Thread_Create_With_CpuSet(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, const CCpuSet *cpuSet)
229 {
230 // new thread in Posix probably inherits affinity from parrent thread
231 Print("Thread_Create_With_CpuSet")
232
233 pthread_attr_t attr;
234 int ret;
235 // int ret2;
236
237 p->_created = 0;
238
239 RINOK(pthread_attr_init(&attr))
240
241 ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
242
243 if (!ret)
244 {
245 if (cpuSet)
246 {
247 #ifdef Z7_AFFINITY_SUPPORTED
248
249 /*
250 printf("\n affinity :");
251 unsigned i;
252 for (i = 0; i < sizeof(*cpuSet) && i < 8; i++)
253 {
254 Byte b = *((const Byte *)cpuSet + i);
255 char temp[32];
256 #define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))))
257 temp[0] = GET_HEX_CHAR((b & 0xF));
258 temp[1] = GET_HEX_CHAR((b >> 4));
259 // temp[0] = GET_HEX_CHAR((b >> 4)); // big-endian
260 // temp[1] = GET_HEX_CHAR((b & 0xF)); // big-endian
261 temp[2] = 0;
262 printf("%s", temp);
263 }
264 printf("\n");
265 */
266
267 // ret2 =
268 pthread_attr_setaffinity_np(&attr, sizeof(*cpuSet), cpuSet);
269 // if (ret2) ret = ret2;
270 #endif
271 }
272
273 ret = pthread_create(&p->_tid, &attr, func, param);
274
275 if (!ret)
276 {
277 p->_created = 1;
278 /*
279 if (cpuSet)
280 {
281 // ret2 =
282 pthread_setaffinity_np(p->_tid, sizeof(*cpuSet), cpuSet);
283 // if (ret2) ret = ret2;
284 }
285 */
286 }
287 }
288 // ret2 =
289 pthread_attr_destroy(&attr);
290 // if (ret2 != 0) ret = ret2;
291 return ret;
292 }
293
294
Thread_Create(CThread * p,THREAD_FUNC_TYPE func,LPVOID param)295 WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
296 {
297 return Thread_Create_With_CpuSet(p, func, param, NULL);
298 }
299
300
Thread_Create_With_Affinity(CThread * p,THREAD_FUNC_TYPE func,LPVOID param,CAffinityMask affinity)301 WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, CAffinityMask affinity)
302 {
303 Print("Thread_Create_WithAffinity")
304 CCpuSet cs;
305 unsigned i;
306 CpuSet_Zero(&cs);
307 for (i = 0; i < sizeof(affinity) * 8; i++)
308 {
309 if (affinity == 0)
310 break;
311 if (affinity & 1)
312 {
313 CpuSet_Set(&cs, i);
314 }
315 affinity >>= 1;
316 }
317 return Thread_Create_With_CpuSet(p, func, param, &cs);
318 }
319
320
Thread_Close(CThread * p)321 WRes Thread_Close(CThread *p)
322 {
323 // Print("Thread_Close")
324 int ret;
325 if (!p->_created)
326 return 0;
327
328 ret = pthread_detach(p->_tid);
329 p->_tid = 0;
330 p->_created = 0;
331 return ret;
332 }
333
334
Thread_Wait_Close(CThread * p)335 WRes Thread_Wait_Close(CThread *p)
336 {
337 // Print("Thread_Wait_Close")
338 void *thread_return;
339 int ret;
340 if (!p->_created)
341 return EINVAL;
342
343 ret = pthread_join(p->_tid, &thread_return);
344 // probably we can't use that (_tid) after pthread_join(), so we close thread here
345 p->_created = 0;
346 p->_tid = 0;
347 return ret;
348 }
349
350
351
Event_Create(CEvent * p,int manualReset,int signaled)352 static WRes Event_Create(CEvent *p, int manualReset, int signaled)
353 {
354 RINOK(pthread_mutex_init(&p->_mutex, NULL))
355 RINOK(pthread_cond_init(&p->_cond, NULL))
356 p->_manual_reset = manualReset;
357 p->_state = (signaled ? True : False);
358 p->_created = 1;
359 return 0;
360 }
361
ManualResetEvent_Create(CManualResetEvent * p,int signaled)362 WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled)
363 { return Event_Create(p, True, signaled); }
ManualResetEvent_CreateNotSignaled(CManualResetEvent * p)364 WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p)
365 { return ManualResetEvent_Create(p, 0); }
AutoResetEvent_Create(CAutoResetEvent * p,int signaled)366 WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled)
367 { return Event_Create(p, False, signaled); }
AutoResetEvent_CreateNotSignaled(CAutoResetEvent * p)368 WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p)
369 { return AutoResetEvent_Create(p, 0); }
370
371
Event_Set(CEvent * p)372 WRes Event_Set(CEvent *p)
373 {
374 RINOK(pthread_mutex_lock(&p->_mutex))
375 p->_state = True;
376 int res1 = pthread_cond_broadcast(&p->_cond);
377 int res2 = pthread_mutex_unlock(&p->_mutex);
378 return (res2 ? res2 : res1);
379 }
380
Event_Reset(CEvent * p)381 WRes Event_Reset(CEvent *p)
382 {
383 RINOK(pthread_mutex_lock(&p->_mutex))
384 p->_state = False;
385 return pthread_mutex_unlock(&p->_mutex);
386 }
387
Event_Wait(CEvent * p)388 WRes Event_Wait(CEvent *p)
389 {
390 RINOK(pthread_mutex_lock(&p->_mutex))
391 while (p->_state == False)
392 {
393 // ETIMEDOUT
394 // ret =
395 pthread_cond_wait(&p->_cond, &p->_mutex);
396 // if (ret != 0) break;
397 }
398 if (p->_manual_reset == False)
399 {
400 p->_state = False;
401 }
402 return pthread_mutex_unlock(&p->_mutex);
403 }
404
Event_Close(CEvent * p)405 WRes Event_Close(CEvent *p)
406 {
407 if (!p->_created)
408 return 0;
409 p->_created = 0;
410 {
411 int res1 = pthread_mutex_destroy(&p->_mutex);
412 int res2 = pthread_cond_destroy(&p->_cond);
413 return (res1 ? res1 : res2);
414 }
415 }
416
417
Semaphore_Create(CSemaphore * p,UInt32 initCount,UInt32 maxCount)418 WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
419 {
420 if (initCount > maxCount || maxCount < 1)
421 return EINVAL;
422 RINOK(pthread_mutex_init(&p->_mutex, NULL))
423 RINOK(pthread_cond_init(&p->_cond, NULL))
424 p->_count = initCount;
425 p->_maxCount = maxCount;
426 p->_created = 1;
427 return 0;
428 }
429
430
Semaphore_OptCreateInit(CSemaphore * p,UInt32 initCount,UInt32 maxCount)431 WRes Semaphore_OptCreateInit(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
432 {
433 if (Semaphore_IsCreated(p))
434 {
435 /*
436 WRes wres = Semaphore_Close(p);
437 if (wres != 0)
438 return wres;
439 */
440 if (initCount > maxCount || maxCount < 1)
441 return EINVAL;
442 // return EINVAL; // for debug
443 p->_count = initCount;
444 p->_maxCount = maxCount;
445 return 0;
446 }
447 return Semaphore_Create(p, initCount, maxCount);
448 }
449
450
Semaphore_ReleaseN(CSemaphore * p,UInt32 releaseCount)451 WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 releaseCount)
452 {
453 UInt32 newCount;
454 int ret;
455
456 if (releaseCount < 1)
457 return EINVAL;
458
459 RINOK(pthread_mutex_lock(&p->_mutex))
460
461 newCount = p->_count + releaseCount;
462 if (newCount > p->_maxCount)
463 ret = ERROR_TOO_MANY_POSTS; // EINVAL;
464 else
465 {
466 p->_count = newCount;
467 ret = pthread_cond_broadcast(&p->_cond);
468 }
469 RINOK(pthread_mutex_unlock(&p->_mutex))
470 return ret;
471 }
472
Semaphore_Wait(CSemaphore * p)473 WRes Semaphore_Wait(CSemaphore *p)
474 {
475 RINOK(pthread_mutex_lock(&p->_mutex))
476 while (p->_count < 1)
477 {
478 pthread_cond_wait(&p->_cond, &p->_mutex);
479 }
480 p->_count--;
481 return pthread_mutex_unlock(&p->_mutex);
482 }
483
Semaphore_Close(CSemaphore * p)484 WRes Semaphore_Close(CSemaphore *p)
485 {
486 if (!p->_created)
487 return 0;
488 p->_created = 0;
489 {
490 int res1 = pthread_mutex_destroy(&p->_mutex);
491 int res2 = pthread_cond_destroy(&p->_cond);
492 return (res1 ? res1 : res2);
493 }
494 }
495
496
497
CriticalSection_Init(CCriticalSection * p)498 WRes CriticalSection_Init(CCriticalSection *p)
499 {
500 // Print("CriticalSection_Init")
501 if (!p)
502 return EINTR;
503 return pthread_mutex_init(&p->_mutex, NULL);
504 }
505
CriticalSection_Enter(CCriticalSection * p)506 void CriticalSection_Enter(CCriticalSection *p)
507 {
508 // Print("CriticalSection_Enter")
509 if (p)
510 {
511 // int ret =
512 pthread_mutex_lock(&p->_mutex);
513 }
514 }
515
CriticalSection_Leave(CCriticalSection * p)516 void CriticalSection_Leave(CCriticalSection *p)
517 {
518 // Print("CriticalSection_Leave")
519 if (p)
520 {
521 // int ret =
522 pthread_mutex_unlock(&p->_mutex);
523 }
524 }
525
CriticalSection_Delete(CCriticalSection * p)526 void CriticalSection_Delete(CCriticalSection *p)
527 {
528 // Print("CriticalSection_Delete")
529 if (p)
530 {
531 // int ret =
532 pthread_mutex_destroy(&p->_mutex);
533 }
534 }
535
InterlockedIncrement(LONG volatile * addend)536 LONG InterlockedIncrement(LONG volatile *addend)
537 {
538 // Print("InterlockedIncrement")
539 #ifdef USE_HACK_UNSAFE_ATOMIC
540 LONG val = *addend + 1;
541 *addend = val;
542 return val;
543 #else
544
545 #if defined(__clang__) && (__clang_major__ >= 8)
546 #pragma GCC diagnostic ignored "-Watomic-implicit-seq-cst"
547 #endif
548 return __sync_add_and_fetch(addend, 1);
549 #endif
550 }
551
552 #endif // _WIN32
553
AutoResetEvent_OptCreate_And_Reset(CAutoResetEvent * p)554 WRes AutoResetEvent_OptCreate_And_Reset(CAutoResetEvent *p)
555 {
556 if (Event_IsCreated(p))
557 return Event_Reset(p);
558 return AutoResetEvent_CreateNotSignaled(p);
559 }
560
561 #undef PRF
562 #undef Print
563