Threads.c 14 KB

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