Skip to content

Commit 9a84f14

Browse files
committed
SMultithreadPlus: make POSIX thread ID handling portable (#22)
- thread_id_t is now platform-specific: pthread_t on POSIX, unsigned long on Win32. Avoids the (void*) cast of pthread_t, which is undefined behaviour on platforms where pthread_t is an opaque struct (macOS, musl). - Add thread_id_equals() which uses pthread_equal() on POSIX and == on Win32, so the deadlock check in requestExitAndWait()/join() is now standard-compliant. - Drop the thread_id_t(-1) sentinel (which is not expressible for an opaque pthread_t) and gate the deadlock check on mRunning instead; the protection against thread-ID recycling is preserved because mRunning is false while no thread is active. - Clean up the now-unnecessary (thread_id_t)/(uint64_t) casts. No external callers depend on the previous void* representation — thread_id_t and getThreadId() are used only inside SMultithreadPlus.
1 parent 8e814ce commit 9a84f14

2 files changed

Lines changed: 37 additions & 14 deletions

File tree

SMultithreadPlus/inc/SThread.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,22 @@
55
#include "SMutex.h"
66
#include "SCondition.h"
77

8-
typedef void * thread_id_t;
8+
#if defined(ANDROID) || defined(__GNUC__)
9+
// pthread_t is opaque on some POSIX implementations (macOS, musl), so we
10+
// keep it as the platform's native pthread_t and compare via pthread_equal.
11+
typedef pthread_t thread_id_t;
12+
#elif defined(_WIN32)
13+
// Equivalent to Win32 DWORD; using unsigned long avoids pulling <windows.h>
14+
// into this public header.
15+
typedef unsigned long thread_id_t;
16+
#else
17+
# error "need to implement for this platform."
18+
#endif
19+
920
typedef int (*thread_func_t)(void *);
1021

22+
bool thread_id_equals(thread_id_t a, thread_id_t b);
23+
1124
// This maps directly to the "nice" priorities we use in Linux.
1225
enum {
1326
STHREAD_PRIORITY_LOWEST = 19,

SMultithreadPlus/src/SThread.cpp

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,20 @@ bool createRawThreadEtc(thread_func_t entryFunction,
122122
// assigned after the child starts. Use memory barrier / lock if the child
123123
// or other threads also need access.
124124
if (threadId != NULL) {
125-
*threadId = (thread_id_t)thread;
125+
*threadId = thread;
126126
}
127127

128128
return true;
129129
}
130130

131131
thread_id_t getThreadId()
132132
{
133-
return (thread_id_t)pthread_self();
133+
return pthread_self();
134+
}
135+
136+
bool thread_id_equals(thread_id_t a, thread_id_t b)
137+
{
138+
return pthread_equal(a, b) != 0;
134139
}
135140

136141
#elif defined(_WIN32)
@@ -178,7 +183,7 @@ static bool doCreateThread(thread_func_t fn, void* arg, thread_id_t *id)
178183
}
179184

180185
if (id != NULL) {
181-
*id = (thread_id_t)(uint64_t)thrdaddr;
186+
*id = thrdaddr;
182187
}
183188

184189
CloseHandle(hThread);
@@ -198,7 +203,12 @@ bool createRawThreadEtc(thread_func_t fn,
198203

199204
thread_id_t getThreadId()
200205
{
201-
return (thread_id_t)(uint64_t)GetCurrentThreadId();
206+
return GetCurrentThreadId();
207+
}
208+
209+
bool thread_id_equals(thread_id_t a, thread_id_t b)
210+
{
211+
return a == b;
202212
}
203213

204214
#else
@@ -210,11 +220,14 @@ thread_id_t getThreadId()
210220
*/
211221

212222
SThread::SThread()
213-
:mThread(thread_id_t(-1)),
223+
:mThread(),
214224
mStatus(SMP_OK),
215225
mExitPending(false),
216226
mRunning(false)
217227
{
228+
// mThread is only read while mRunning is true, so its initial value is
229+
// irrelevant; value-initialisation above keeps it deterministic on the
230+
// pointer/integer pthread_t variants we support.
218231
}
219232

220233
SThread::~SThread()
@@ -234,7 +247,6 @@ SmpError SThread::run(const char* name, int priority, int stack)
234247
// try again after an error happened (either below, or in readyToRun())
235248
mStatus = SMP_OK;
236249
mExitPending = false;
237-
mThread = thread_id_t(-1);
238250
mRunning = true;
239251

240252
bool res;
@@ -243,8 +255,6 @@ SmpError SThread::run(const char* name, int priority, int stack)
243255
if (res == false) {
244256
mStatus = SMP_UNKNOWN; // something happened!
245257
mRunning = false;
246-
mThread = thread_id_t(-1);
247-
248258
return SMP_UNKNOWN;
249259
}
250260

@@ -298,9 +308,9 @@ int SThread::_threadLoop(void* user)
298308
if (result == false || self->mExitPending) {
299309
self->mExitPending = true;
300310
self->mRunning = false;
301-
// clear thread ID so that requestExitAndWait() does not exit if
302-
// called by a new thread using the same thread ID as this one.
303-
self->mThread = thread_id_t(-1);
311+
// mRunning gates reads of mThread, so the deadlock check in
312+
// requestExitAndWait()/join() will not false-match a new thread
313+
// that happens to be assigned the same kernel thread ID.
304314
// note that interested observers blocked in requestExitAndWait are
305315
// awoken by broadcast, but blocked on mLock until break exits scope
306316
self->mThreadExitedCondition.broadcast();
@@ -323,7 +333,7 @@ int SThread::requestExitAndWait()
323333
{
324334
SMutex::Autolock _l(mLock);
325335

326-
if (mThread == getThreadId()) {
336+
if (mRunning && thread_id_equals(mThread, getThreadId())) {
327337
LOGWRN(
328338
"Thread (this=%p): don't call waitForExit() from this "
329339
"Thread object's thread. It's a guaranteed deadlock!",
@@ -348,7 +358,7 @@ int SThread::join()
348358
{
349359
SMutex::Autolock _l(mLock);
350360

351-
if (mThread == getThreadId()) {
361+
if (mRunning && thread_id_equals(mThread, getThreadId())) {
352362
LOGWRN(
353363
"Thread (this=%p): don't call join() from this "
354364
"Thread object's thread. It's a guaranteed deadlock!",

0 commit comments

Comments
 (0)