| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- /* Threads.c -- multithreading library
- 2024-03-28 : Igor Pavlov : Public domain */
- #include "Precomp.h"
- #ifdef _WIN32
- #ifndef USE_THREADS_CreateThread
- #include <process.h>
- #endif
- #include "Threads.h"
- static WRes GetError(void)
- {
- const DWORD res = GetLastError();
- return res ? (WRes)res : 1;
- }
- static WRes HandleToWRes(HANDLE h) { return (h != NULL) ? 0 : GetError(); }
- static WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); }
- WRes HandlePtr_Close(HANDLE *p)
- {
- if (*p != NULL)
- {
- if (!CloseHandle(*p))
- return GetError();
- *p = NULL;
- }
- return 0;
- }
- WRes Handle_WaitObject(HANDLE h)
- {
- DWORD dw = WaitForSingleObject(h, INFINITE);
- /*
- (dw) result:
- WAIT_OBJECT_0 // 0
- WAIT_ABANDONED // 0x00000080 : is not compatible with Win32 Error space
- WAIT_TIMEOUT // 0x00000102 : is compatible with Win32 Error space
- WAIT_FAILED // 0xFFFFFFFF
- */
- if (dw == WAIT_FAILED)
- {
- dw = GetLastError();
- if (dw == 0)
- return WAIT_FAILED;
- }
- return (WRes)dw;
- }
- #define Thread_Wait(p) Handle_WaitObject(*(p))
- WRes Thread_Wait_Close(CThread *p)
- {
- WRes res = Thread_Wait(p);
- WRes res2 = Thread_Close(p);
- return (res != 0 ? res : res2);
- }
- WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
- {
- /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
- #ifdef USE_THREADS_CreateThread
- DWORD threadId;
- *p = CreateThread(NULL, 0, func, param, 0, &threadId);
-
- #else
-
- unsigned threadId;
- *p = (HANDLE)(_beginthreadex(NULL, 0, func, param, 0, &threadId));
-
- #endif
- /* maybe we must use errno here, but probably GetLastError() is also OK. */
- return HandleToWRes(*p);
- }
- WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, CAffinityMask affinity)
- {
- #ifdef USE_THREADS_CreateThread
- UNUSED_VAR(affinity)
- return Thread_Create(p, func, param);
-
- #else
-
- /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
- HANDLE h;
- WRes wres;
- unsigned threadId;
- h = (HANDLE)(_beginthreadex(NULL, 0, func, param, CREATE_SUSPENDED, &threadId));
- *p = h;
- wres = HandleToWRes(h);
- if (h)
- {
- {
- // DWORD_PTR prevMask =
- SetThreadAffinityMask(h, (DWORD_PTR)affinity);
- /*
- if (prevMask == 0)
- {
- // affinity change is non-critical error, so we can ignore it
- // wres = GetError();
- }
- */
- }
- {
- DWORD prevSuspendCount = ResumeThread(h);
- /* ResumeThread() returns:
- 0 : was_not_suspended
- 1 : was_resumed
- -1 : error
- */
- if (prevSuspendCount == (DWORD)-1)
- wres = GetError();
- }
- }
- /* maybe we must use errno here, but probably GetLastError() is also OK. */
- return wres;
- #endif
- }
- static WRes Event_Create(CEvent *p, BOOL manualReset, int signaled)
- {
- *p = CreateEvent(NULL, manualReset, (signaled ? TRUE : FALSE), NULL);
- return HandleToWRes(*p);
- }
- WRes Event_Set(CEvent *p) { return BOOLToWRes(SetEvent(*p)); }
- WRes Event_Reset(CEvent *p) { return BOOLToWRes(ResetEvent(*p)); }
- WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled) { return Event_Create(p, TRUE, signaled); }
- WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled) { return Event_Create(p, FALSE, signaled); }
- WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) { return ManualResetEvent_Create(p, 0); }
- WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) { return AutoResetEvent_Create(p, 0); }
- WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
- {
- // negative ((LONG)maxCount) is not supported in WIN32::CreateSemaphore()
- *p = CreateSemaphore(NULL, (LONG)initCount, (LONG)maxCount, NULL);
- return HandleToWRes(*p);
- }
- WRes Semaphore_OptCreateInit(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
- {
- // if (Semaphore_IsCreated(p))
- {
- WRes wres = Semaphore_Close(p);
- if (wres != 0)
- return wres;
- }
- return Semaphore_Create(p, initCount, maxCount);
- }
- static WRes Semaphore_Release(CSemaphore *p, LONG releaseCount, LONG *previousCount)
- { return BOOLToWRes(ReleaseSemaphore(*p, releaseCount, previousCount)); }
- WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num)
- { return Semaphore_Release(p, (LONG)num, NULL); }
- WRes Semaphore_Release1(CSemaphore *p) { return Semaphore_ReleaseN(p, 1); }
- WRes CriticalSection_Init(CCriticalSection *p)
- {
- /* InitializeCriticalSection() can raise exception:
- Windows XP, 2003 : can raise a STATUS_NO_MEMORY exception
- Windows Vista+ : no exceptions */
- #ifdef _MSC_VER
- #ifdef __clang__
- #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
- #endif
- __try
- #endif
- {
- InitializeCriticalSection(p);
- /* InitializeCriticalSectionAndSpinCount(p, 0); */
- }
- #ifdef _MSC_VER
- __except (EXCEPTION_EXECUTE_HANDLER) { return ERROR_NOT_ENOUGH_MEMORY; }
- #endif
- return 0;
- }
- #else // _WIN32
- // ---------- POSIX ----------
- #if defined(__linux__) && !defined(__APPLE__) && !defined(_AIX) && !defined(__ANDROID__)
- #ifndef Z7_AFFINITY_DISABLE
- // _GNU_SOURCE can be required for pthread_setaffinity_np() / CPU_ZERO / CPU_SET
- // clang < 3.6 : unknown warning group '-Wreserved-id-macro'
- // clang 3.6 - 12.01 : gives warning "macro name is a reserved identifier"
- // clang >= 13 : do not give warning
- #if !defined(_GNU_SOURCE)
- Z7_DIAGNOSTIC_IGNORE_BEGIN_RESERVED_MACRO_IDENTIFIER
- // #define _GNU_SOURCE
- Z7_DIAGNOSTIC_IGNORE_END_RESERVED_MACRO_IDENTIFIER
- #endif // !defined(_GNU_SOURCE)
- #endif // Z7_AFFINITY_DISABLE
- #endif // __linux__
- #include "Threads.h"
- #include <errno.h>
- #include <stdlib.h>
- #include <string.h>
- #ifdef Z7_AFFINITY_SUPPORTED
- // #include <sched.h>
- #endif
- // #include <stdio.h>
- // #define PRF(p) p
- #define PRF(p)
- #define Print(s) PRF(printf("\n%s\n", s);)
- WRes Thread_Create_With_CpuSet(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, const CCpuSet *cpuSet)
- {
- // new thread in Posix probably inherits affinity from parrent thread
- Print("Thread_Create_With_CpuSet")
- pthread_attr_t attr;
- int ret;
- // int ret2;
- p->_created = 0;
- RINOK(pthread_attr_init(&attr))
- ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- if (!ret)
- {
- if (cpuSet)
- {
- // pthread_attr_setaffinity_np() is not supported for MUSL compile.
- // so we check for __GLIBC__ here
- #if defined(Z7_AFFINITY_SUPPORTED) && defined( __GLIBC__)
- /*
- printf("\n affinity :");
- unsigned i;
- for (i = 0; i < sizeof(*cpuSet) && i < 8; i++)
- {
- Byte b = *((const Byte *)cpuSet + i);
- char temp[32];
- #define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))))
- temp[0] = GET_HEX_CHAR((b & 0xF));
- temp[1] = GET_HEX_CHAR((b >> 4));
- // temp[0] = GET_HEX_CHAR((b >> 4)); // big-endian
- // temp[1] = GET_HEX_CHAR((b & 0xF)); // big-endian
- temp[2] = 0;
- printf("%s", temp);
- }
- printf("\n");
- */
- // ret2 =
- pthread_attr_setaffinity_np(&attr, sizeof(*cpuSet), cpuSet);
- // if (ret2) ret = ret2;
- #endif
- }
-
- ret = pthread_create(&p->_tid, &attr, func, param);
-
- if (!ret)
- {
- p->_created = 1;
- /*
- if (cpuSet)
- {
- // ret2 =
- pthread_setaffinity_np(p->_tid, sizeof(*cpuSet), cpuSet);
- // if (ret2) ret = ret2;
- }
- */
- }
- }
- // ret2 =
- pthread_attr_destroy(&attr);
- // if (ret2 != 0) ret = ret2;
- return ret;
- }
- WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
- {
- return Thread_Create_With_CpuSet(p, func, param, NULL);
- }
- WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, CAffinityMask affinity)
- {
- Print("Thread_Create_WithAffinity")
- CCpuSet cs;
- unsigned i;
- CpuSet_Zero(&cs);
- for (i = 0; i < sizeof(affinity) * 8; i++)
- {
- if (affinity == 0)
- break;
- if (affinity & 1)
- {
- CpuSet_Set(&cs, i);
- }
- affinity >>= 1;
- }
- return Thread_Create_With_CpuSet(p, func, param, &cs);
- }
- WRes Thread_Close(CThread *p)
- {
- // Print("Thread_Close")
- int ret;
- if (!p->_created)
- return 0;
-
- ret = pthread_detach(p->_tid);
- p->_tid = 0;
- p->_created = 0;
- return ret;
- }
- WRes Thread_Wait_Close(CThread *p)
- {
- // Print("Thread_Wait_Close")
- void *thread_return;
- int ret;
- if (!p->_created)
- return EINVAL;
- ret = pthread_join(p->_tid, &thread_return);
- // probably we can't use that (_tid) after pthread_join(), so we close thread here
- p->_created = 0;
- p->_tid = 0;
- return ret;
- }
- static WRes Event_Create(CEvent *p, int manualReset, int signaled)
- {
- RINOK(pthread_mutex_init(&p->_mutex, NULL))
- RINOK(pthread_cond_init(&p->_cond, NULL))
- p->_manual_reset = manualReset;
- p->_state = (signaled ? True : False);
- p->_created = 1;
- return 0;
- }
- WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled)
- { return Event_Create(p, True, signaled); }
- WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p)
- { return ManualResetEvent_Create(p, 0); }
- WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled)
- { return Event_Create(p, False, signaled); }
- WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p)
- { return AutoResetEvent_Create(p, 0); }
- #if defined(Z7_LLVM_CLANG_VERSION) && (__clang_major__ == 13)
- // freebsd:
- #pragma GCC diagnostic ignored "-Wthread-safety-analysis"
- #endif
- WRes Event_Set(CEvent *p)
- {
- RINOK(pthread_mutex_lock(&p->_mutex))
- p->_state = True;
- {
- const int res1 = pthread_cond_broadcast(&p->_cond);
- const int res2 = pthread_mutex_unlock(&p->_mutex);
- return (res2 ? res2 : res1);
- }
- }
- WRes Event_Reset(CEvent *p)
- {
- RINOK(pthread_mutex_lock(&p->_mutex))
- p->_state = False;
- return pthread_mutex_unlock(&p->_mutex);
- }
-
- WRes Event_Wait(CEvent *p)
- {
- RINOK(pthread_mutex_lock(&p->_mutex))
- while (p->_state == False)
- {
- // ETIMEDOUT
- // ret =
- pthread_cond_wait(&p->_cond, &p->_mutex);
- // if (ret != 0) break;
- }
- if (p->_manual_reset == False)
- {
- p->_state = False;
- }
- return pthread_mutex_unlock(&p->_mutex);
- }
- WRes Event_Close(CEvent *p)
- {
- if (!p->_created)
- return 0;
- p->_created = 0;
- {
- const int res1 = pthread_mutex_destroy(&p->_mutex);
- const int res2 = pthread_cond_destroy(&p->_cond);
- return (res1 ? res1 : res2);
- }
- }
- WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
- {
- if (initCount > maxCount || maxCount < 1)
- return EINVAL;
- RINOK(pthread_mutex_init(&p->_mutex, NULL))
- RINOK(pthread_cond_init(&p->_cond, NULL))
- p->_count = initCount;
- p->_maxCount = maxCount;
- p->_created = 1;
- return 0;
- }
- WRes Semaphore_OptCreateInit(CSemaphore *p, UInt32 initCount, UInt32 maxCount)
- {
- if (Semaphore_IsCreated(p))
- {
- /*
- WRes wres = Semaphore_Close(p);
- if (wres != 0)
- return wres;
- */
- if (initCount > maxCount || maxCount < 1)
- return EINVAL;
- // return EINVAL; // for debug
- p->_count = initCount;
- p->_maxCount = maxCount;
- return 0;
- }
- return Semaphore_Create(p, initCount, maxCount);
- }
- WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 releaseCount)
- {
- UInt32 newCount;
- int ret;
- if (releaseCount < 1)
- return EINVAL;
- RINOK(pthread_mutex_lock(&p->_mutex))
- newCount = p->_count + releaseCount;
- if (newCount > p->_maxCount)
- ret = ERROR_TOO_MANY_POSTS; // EINVAL;
- else
- {
- p->_count = newCount;
- ret = pthread_cond_broadcast(&p->_cond);
- }
- RINOK(pthread_mutex_unlock(&p->_mutex))
- return ret;
- }
- WRes Semaphore_Wait(CSemaphore *p)
- {
- RINOK(pthread_mutex_lock(&p->_mutex))
- while (p->_count < 1)
- {
- pthread_cond_wait(&p->_cond, &p->_mutex);
- }
- p->_count--;
- return pthread_mutex_unlock(&p->_mutex);
- }
- WRes Semaphore_Close(CSemaphore *p)
- {
- if (!p->_created)
- return 0;
- p->_created = 0;
- {
- const int res1 = pthread_mutex_destroy(&p->_mutex);
- const int res2 = pthread_cond_destroy(&p->_cond);
- return (res1 ? res1 : res2);
- }
- }
- WRes CriticalSection_Init(CCriticalSection *p)
- {
- // Print("CriticalSection_Init")
- if (!p)
- return EINTR;
- return pthread_mutex_init(&p->_mutex, NULL);
- }
- void CriticalSection_Enter(CCriticalSection *p)
- {
- // Print("CriticalSection_Enter")
- if (p)
- {
- // int ret =
- pthread_mutex_lock(&p->_mutex);
- }
- }
- void CriticalSection_Leave(CCriticalSection *p)
- {
- // Print("CriticalSection_Leave")
- if (p)
- {
- // int ret =
- pthread_mutex_unlock(&p->_mutex);
- }
- }
- void CriticalSection_Delete(CCriticalSection *p)
- {
- // Print("CriticalSection_Delete")
- if (p)
- {
- // int ret =
- pthread_mutex_destroy(&p->_mutex);
- }
- }
- LONG InterlockedIncrement(LONG volatile *addend)
- {
- // Print("InterlockedIncrement")
- #ifdef USE_HACK_UNSAFE_ATOMIC
- LONG val = *addend + 1;
- *addend = val;
- return val;
- #else
- #if defined(__clang__) && (__clang_major__ >= 8)
- #pragma GCC diagnostic ignored "-Watomic-implicit-seq-cst"
- #endif
- return __sync_add_and_fetch(addend, 1);
- #endif
- }
- LONG InterlockedDecrement(LONG volatile *addend)
- {
- // Print("InterlockedDecrement")
- #ifdef USE_HACK_UNSAFE_ATOMIC
- LONG val = *addend - 1;
- *addend = val;
- return val;
- #else
- return __sync_sub_and_fetch(addend, 1);
- #endif
- }
- #endif // _WIN32
- WRes AutoResetEvent_OptCreate_And_Reset(CAutoResetEvent *p)
- {
- if (Event_IsCreated(p))
- return Event_Reset(p);
- return AutoResetEvent_CreateNotSignaled(p);
- }
- #undef PRF
- #undef Print
|