c11: Implement and use of thrd_current properly on win32

Now thrd_t are a struct that contains both thread handle and thread id.
For threads that not created by thrd_create, thrd_current are returning pseudo thread handle;
but we can still compare threads differences by using thread id.

Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17071>
This commit is contained in:
Yonggang Luo 2022-04-20 04:13:25 +08:00 committed by Marge Bot
parent 125a952b66
commit e7b4af434b
6 changed files with 55 additions and 100 deletions

View file

@ -31,6 +31,7 @@
#include <errno.h>
#include <process.h> // MSVCRT
#include <stdlib.h>
#include <stdbool.h>
#include "c11/threads.h"
@ -83,13 +84,24 @@ Implementation limits:
struct impl_thrd_param {
thrd_start_t func;
void *arg;
thrd_t thrd;
};
struct thrd_state {
thrd_t thrd;
bool handle_need_close;
};
static thread_local struct thrd_state impl_current_thread = { 0 };
static unsigned __stdcall impl_thrd_routine(void *p)
{
struct impl_thrd_param *pack_p = (struct impl_thrd_param *)p;
struct impl_thrd_param pack;
int code;
memcpy(&pack, p, sizeof(struct impl_thrd_param));
impl_current_thread.thrd = pack_p->thrd;
impl_current_thread.handle_need_close = false;
memcpy(&pack, pack_p, sizeof(struct impl_thrd_param));
free(p);
code = pack.func(pack.arg);
return (unsigned)code;
@ -307,7 +319,12 @@ mtx_unlock(mtx_t *mtx)
void
__threads_win32_tls_callback(void)
{
struct thrd_state *state = &impl_current_thread;
impl_tss_dtor_invoke();
if (state->handle_need_close) {
state->handle_need_close = false;
CloseHandle(state->thrd.handle);
}
}
/*------------------- 7.25.5 Thread functions -------------------*/
@ -322,25 +339,23 @@ thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
if (!pack) return thrd_nomem;
pack->func = func;
pack->arg = arg;
handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL);
handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, CREATE_SUSPENDED, NULL);
if (handle == 0) {
free(pack);
if (errno == EAGAIN || errno == EACCES)
return thrd_nomem;
return thrd_error;
}
*thr = (thrd_t)handle;
thr->handle = (void*)handle;
pack->thrd = *thr;
ResumeThread((HANDLE)handle);
return thrd_success;
}
#if 0
// 7.25.5.2
static inline thrd_t
thrd_t
thrd_current(void)
{
HANDLE hCurrentThread;
BOOL bRet;
/* GetCurrentThread() returns a pseudo-handle, which we need
* to pass to DuplicateHandle(). Only the resulting handle can be used
* from other threads.
@ -358,27 +373,24 @@ thrd_current(void)
* Life would be much easier if C11 threads had different abstractions for
* threads and thread IDs, just like C++11 threads does...
*/
bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle
GetCurrentThread(), // source (pseudo) handle
GetCurrentProcess(), // target process
&hCurrentThread, // target handle
0,
FALSE,
DUPLICATE_SAME_ACCESS);
assert(bRet);
if (!bRet) {
hCurrentThread = GetCurrentThread();
struct thrd_state *state = &impl_current_thread;
if (state->thrd.handle == NULL)
{
if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
&(state->thrd.handle), 0, FALSE, DUPLICATE_SAME_ACCESS))
{
abort();
}
state->handle_need_close = true;
}
return hCurrentThread;
return state->thrd;
}
#endif
// 7.25.5.3
int
thrd_detach(thrd_t thr)
{
CloseHandle(thr);
CloseHandle(thr.handle);
return thrd_success;
}
@ -386,7 +398,7 @@ thrd_detach(thrd_t thr)
int
thrd_equal(thrd_t thr0, thrd_t thr1)
{
return GetThreadId(thr0) == GetThreadId(thr1);
return GetThreadId(thr0.handle) == GetThreadId(thr1.handle);
}
// 7.25.5.5
@ -402,17 +414,20 @@ int
thrd_join(thrd_t thr, int *res)
{
DWORD w, code;
w = WaitForSingleObject(thr, INFINITE);
if (thr.handle == NULL) {
return thrd_error;
}
w = WaitForSingleObject(thr.handle, INFINITE);
if (w != WAIT_OBJECT_0)
return thrd_error;
if (res) {
if (!GetExitCodeThread(thr, &code)) {
CloseHandle(thr);
if (!GetExitCodeThread(thr.handle, &code)) {
CloseHandle(thr.handle);
return thrd_error;
}
*res = (int)code;
}
CloseHandle(thr);
CloseHandle(thr.handle);
return thrd_success;
}

View file

@ -88,7 +88,11 @@ typedef struct
{
void *Ptr;
} cnd_t;
typedef void *thrd_t;
/* Define thrd_t as struct type intentionally for avoid use of thrd_t as pointer type */
typedef struct
{
void *handle;
} thrd_t;
typedef unsigned long tss_t;
typedef struct
{

View file

@ -97,7 +97,7 @@ static void
tc_set_driver_thread(struct threaded_context *tc)
{
#ifndef NDEBUG
tc->driver_thread = util_get_thread_id();
tc->driver_thread = thrd_current();
#endif
}

View file

@ -485,7 +485,7 @@ struct threaded_context {
* there are cases where the queue is flushed directly
* from the frontend thread
*/
thread_id driver_thread;
thrd_t driver_thread;
#endif
bool seen_tcs;
@ -600,7 +600,7 @@ tc_assert_driver_thread(struct threaded_context *tc)
if (!tc)
return;
#ifndef NDEBUG
assert(util_thread_id_equal(tc->driver_thread, util_get_thread_id()));
assert(u_thread_is_self(tc->driver_thread));
#endif
}

View file

@ -1337,7 +1337,7 @@ lp_rast_destroy(struct lp_rasterizer *rast)
* other threads when returning from main.
*/
DWORD exit_code = STILL_ACTIVE;
if (GetExitCodeThread(rast->threads[i], &exit_code) &&
if (GetExitCodeThread(rast->threads[i].handle, &exit_code) &&
exit_code == STILL_ACTIVE) {
pipe_semaphore_wait(&rast->tasks[i].work_done);
}

View file

@ -197,7 +197,7 @@ util_set_thread_affinity(thrd_t thread,
if (sizeof(m) > 4 && num_mask_bits > 32)
m |= (uint64_t)mask[1] << 32;
m = SetThreadAffinityMask(thread, m);
m = SetThreadAffinityMask(thread.handle, m);
if (!m)
return false;
@ -221,21 +221,10 @@ util_set_current_thread_affinity(const uint32_t *mask,
uint32_t *old_mask,
unsigned num_mask_bits)
{
#if defined(HAVE_PTHREAD_SETAFFINITY)
return util_set_thread_affinity(pthread_self(), mask, old_mask,
return util_set_thread_affinity(thrd_current(), mask, old_mask,
num_mask_bits);
#elif defined(_WIN32) && !defined(__CYGWIN__)
/* The GetCurrentThreadId() handle is only valid within the current thread. */
return util_set_thread_affinity(GetCurrentThread(), mask, old_mask,
num_mask_bits);
#else
return false;
#endif
}
/*
* Thread statistics.
*/
@ -261,24 +250,12 @@ util_thread_get_time_nano(thrd_t thread)
static inline int64_t
util_current_thread_get_time_nano(void)
{
#if defined(HAVE_PTHREAD)
return util_thread_get_time_nano(pthread_self());
#elif defined(_WIN32) && !defined(__CYGWIN__)
/* The GetCurrentThreadId() handle is only valid within the current thread. */
return util_thread_get_time_nano(GetCurrentThread());
#else
return 0;
#endif
return util_thread_get_time_nano(thrd_current());
}
static inline bool u_thread_is_self(thrd_t thread)
{
#if defined(HAVE_PTHREAD)
return pthread_equal(pthread_self(), thread);
#endif
return false;
return thrd_equal(thrd_current(), thread);
}
/*
@ -357,45 +334,4 @@ static inline bool util_barrier_wait(util_barrier *barrier)
#endif
/*
* Thread-id's.
*
* thrd_current() is not portable to windows (or at least not in a desirable
* way), so thread_id's provide an alternative mechanism
*/
#ifdef _WIN32
typedef DWORD thread_id;
#else
typedef thrd_t thread_id;
#endif
static inline thread_id
util_get_thread_id(void)
{
/*
* XXX: Callers of of this function assume it is a lightweight function.
* But unfortunately C11's thrd_current() gives no such guarantees. In
* fact, it's pretty hard to have a compliant implementation of
* thrd_current() on Windows with such characteristics. So for now, we
* side-step this mess and use Windows thread primitives directly here.
*/
#ifdef _WIN32
return GetCurrentThreadId();
#else
return thrd_current();
#endif
}
static inline int
util_thread_id_equal(thread_id t1, thread_id t2)
{
#ifdef _WIN32
return t1 == t2;
#else
return thrd_equal(t1, t2);
#endif
}
#endif /* U_THREAD_H_ */