1/* Threads.c -- multithreading library 22023-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 14static WRes GetError(void) 15{ 16 const DWORD res = GetLastError(); 17 return res ? (WRes)res : 1; 18} 19 20static WRes HandleToWRes(HANDLE h) { return (h != NULL) ? 0 : GetError(); } 21static WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); } 22 23WRes 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 34WRes 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 55WRes 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 62WRes 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 83WRes 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 131static 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 137WRes Event_Set(CEvent *p) { return BOOLToWRes(SetEvent(*p)); } 138WRes Event_Reset(CEvent *p) { return BOOLToWRes(ResetEvent(*p)); } 139 140WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled) { return Event_Create(p, TRUE, signaled); } 141WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled) { return Event_Create(p, FALSE, signaled); } 142WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) { return ManualResetEvent_Create(p, 0); } 143WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) { return AutoResetEvent_Create(p, 0); } 144 145 146WRes 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 153WRes 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 164static WRes Semaphore_Release(CSemaphore *p, LONG releaseCount, LONG *previousCount) 165 { return BOOLToWRes(ReleaseSemaphore(*p, releaseCount, previousCount)); } 166WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num) 167 { return Semaphore_Release(p, (LONG)num, NULL); } 168WRes Semaphore_Release1(CSemaphore *p) { return Semaphore_ReleaseN(p, 1); } 169 170WRes 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 228WRes 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 295WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param) 296{ 297 return Thread_Create_With_CpuSet(p, func, param, NULL); 298} 299 300 301WRes 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 321WRes 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 335WRes 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 352static 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 362WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled) 363 { return Event_Create(p, True, signaled); } 364WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) 365 { return ManualResetEvent_Create(p, 0); } 366WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled) 367 { return Event_Create(p, False, signaled); } 368WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) 369 { return AutoResetEvent_Create(p, 0); } 370 371 372WRes 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 381WRes 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 388WRes 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 405WRes 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 418WRes 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 431WRes 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 451WRes 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 473WRes 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 484WRes 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 498WRes 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 506void 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 516void 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 526void 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 536LONG 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 554WRes 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