forked from 0ad/0ad
pthread: add pthread_once and thread-local storage
This was SVN commit r2123.
This commit is contained in:
parent
9fd08fb044
commit
c179210519
@ -25,6 +25,7 @@
|
||||
#include "lib.h"
|
||||
#include "posix.h"
|
||||
#include "win_internal.h"
|
||||
#include "lockless.h"
|
||||
|
||||
|
||||
static HANDLE pthread_t_to_HANDLE(pthread_t p)
|
||||
@ -38,12 +39,26 @@ static pthread_t HANDLE_to_pthread_t(HANDLE h)
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// misc
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pthread_t pthread_self(void)
|
||||
{
|
||||
return HANDLE_to_pthread_t(GetCurrentThread());
|
||||
}
|
||||
|
||||
|
||||
int pthread_once(pthread_once_t* once, void (*init_routine)(void))
|
||||
{
|
||||
if(CAS(once, 0, 1))
|
||||
init_routine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param)
|
||||
{
|
||||
if(policy)
|
||||
@ -81,6 +96,139 @@ int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// thread-local storage
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// greatest amount of TLS slots any Windows version provides;
|
||||
// used to validate indices.
|
||||
static const uint TLS_LIMIT = 1088;
|
||||
|
||||
// rationale: don't use an array of dtors for every possible TLS slot.
|
||||
// other DLLs may allocate any number of them in their DllMain, so the
|
||||
// array would have to be quite large. instead, store both key and dtor -
|
||||
// we are thus limited only by pthread_key_create calls (which we control).
|
||||
static const uint MAX_DTORS = 4;
|
||||
static struct
|
||||
{
|
||||
pthread_key_t key;
|
||||
void(*dtor)(void*);
|
||||
}
|
||||
dtors[MAX_DTORS];
|
||||
|
||||
|
||||
int pthread_key_create(pthread_key_t* key, void (*dtor)(void*))
|
||||
{
|
||||
DWORD idx = TlsAlloc();
|
||||
if(idx == TLS_OUT_OF_INDEXES)
|
||||
return -ENOMEM;
|
||||
|
||||
assert2(idx < TLS_LIMIT);
|
||||
*key = (pthread_key_t)idx;
|
||||
|
||||
// store dtor
|
||||
for(uint i = 0; i < MAX_DTORS; i++)
|
||||
// .. successfully acquired the slot
|
||||
if(CAS(&dtors[i].dtor, 0, dtor))
|
||||
{
|
||||
dtors[i].key = *key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// not enough slots; we have a valid key, but its dtor won't be called.
|
||||
debug_warn("increase pthread MAX_DTORS");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int pthread_key_delete(pthread_key_t key)
|
||||
{
|
||||
DWORD idx = (DWORD)key;
|
||||
assert2(idx < TLS_LIMIT);
|
||||
|
||||
BOOL ret = TlsFree(idx);
|
||||
assert2(ret != 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void* pthread_getspecific(pthread_key_t key)
|
||||
{
|
||||
DWORD idx = (DWORD)key;
|
||||
assert2(idx < TLS_LIMIT);
|
||||
|
||||
// TlsGetValue sets last error to 0 on success (boo).
|
||||
// don't want this to hide previous error, so restore.
|
||||
DWORD last_err = GetLastError();
|
||||
|
||||
void* data = TlsGetValue(idx);
|
||||
|
||||
// no error
|
||||
if(GetLastError() == 0)
|
||||
{
|
||||
// we care about performance here. SetLastError is low overhead,
|
||||
// but last error = 0 is expected.
|
||||
if(last_err != 0)
|
||||
SetLastError(last_err);
|
||||
}
|
||||
else
|
||||
debug_warn("pthread_getspecific: TlsGetValue failed");
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
int pthread_setspecific(pthread_key_t key, const void* value)
|
||||
{
|
||||
DWORD idx = (DWORD)key;
|
||||
assert2(idx < TLS_LIMIT);
|
||||
|
||||
BOOL ret = TlsSetValue(idx, (void*)value);
|
||||
assert2(ret != 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void call_tls_dtors()
|
||||
{
|
||||
bool had_valid_tls = false;
|
||||
// until all TLS slots have been reset:
|
||||
do
|
||||
{
|
||||
// for each registered dtor: (call order unspecified by SUSv3)
|
||||
for(uint i = 0; i < MAX_DTORS; i++)
|
||||
{
|
||||
void(*dtor)(void*) = dtors[i].dtor;
|
||||
if(!dtor)
|
||||
continue;
|
||||
|
||||
const pthread_key_t key = dtors[i].key;
|
||||
void* tls = pthread_getspecific(key);
|
||||
if(tls)
|
||||
{
|
||||
int ret = pthread_setspecific(key, 0);
|
||||
assert2(ret == 0);
|
||||
|
||||
dtor(tls);
|
||||
had_valid_tls = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(had_valid_tls);
|
||||
|
||||
// rationale: SUSv3 says we're allowed to loop infinitely. we do so to
|
||||
// expose any dtor bugs - this shouldn't normally happen.
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// threads
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ThreadParam
|
||||
{
|
||||
void*(*func)(void*);
|
||||
@ -102,6 +250,9 @@ static unsigned __stdcall thread_start(void* param)
|
||||
// workaround for stupid "void* -> unsigned cast" warning
|
||||
union { void* p; unsigned u; } v;
|
||||
v.p = func(user_arg);
|
||||
|
||||
call_tls_dtors();
|
||||
|
||||
return v.u;
|
||||
}
|
||||
|
||||
@ -156,7 +307,10 @@ int pthread_join(pthread_t thread, void** value_ptr)
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// locks
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// rationale: CRITICAL_SECTIONS have less overhead than Win32 Mutex.
|
||||
// disadvantage is that pthread_mutex_timedlock isn't supported, but
|
||||
|
@ -40,39 +40,52 @@ enum
|
||||
SCHED_OTHER
|
||||
};
|
||||
|
||||
// changing will break pthread_setschedparam:
|
||||
#define sched_get_priority_max(policy) +2
|
||||
#define sched_get_priority_min(policy) -2
|
||||
// changing will break pthread_setschedparam
|
||||
|
||||
|
||||
//
|
||||
// <pthread.h>
|
||||
//
|
||||
|
||||
// one-time init
|
||||
typedef uintptr_t pthread_once_t;
|
||||
#define PTHREAD_ONCE_INIT 0 // static pthread_once_t x = PTHREAD_ONCE_INIT;
|
||||
|
||||
extern int pthread_once(pthread_once_t*, void (*init_routine)(void));
|
||||
|
||||
// thread
|
||||
typedef unsigned int pthread_t;
|
||||
|
||||
extern pthread_t pthread_self(void);
|
||||
extern int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param);
|
||||
extern int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param);
|
||||
|
||||
extern int pthread_create(pthread_t* thread, const void* attr, void*(*func)(void*), void* arg);
|
||||
extern int pthread_cancel(pthread_t thread);
|
||||
extern int pthread_join(pthread_t thread, void** value_ptr);
|
||||
|
||||
// mutex
|
||||
typedef void* pthread_mutex_t; // pointer to critical section
|
||||
typedef void pthread_mutexattr_t;
|
||||
|
||||
extern pthread_mutex_t pthread_mutex_initializer(void);
|
||||
#define PTHREAD_MUTEX_INITIALIZER pthread_mutex_initializer()
|
||||
|
||||
extern pthread_mutex_t pthread_mutex_initializer(void);
|
||||
extern int pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*);
|
||||
extern int pthread_mutex_destroy(pthread_mutex_t*);
|
||||
|
||||
extern int pthread_mutex_lock(pthread_mutex_t*);
|
||||
extern int pthread_mutex_trylock(pthread_mutex_t*);
|
||||
extern int pthread_mutex_unlock(pthread_mutex_t*);
|
||||
extern int pthread_mutex_timedlock(pthread_mutex_t*, const struct timespec*);
|
||||
|
||||
// thread-local storage
|
||||
typedef unsigned int pthread_key_t;
|
||||
|
||||
extern int pthread_key_create(pthread_key_t*, void (*dtor)(void*));
|
||||
extern int pthread_key_delete(pthread_key_t);
|
||||
extern void* pthread_getspecific(pthread_key_t);
|
||||
extern int pthread_setspecific(pthread_key_t, const void* value);
|
||||
|
||||
|
||||
//
|
||||
// <semaphore.h>
|
||||
|
Loading…
Reference in New Issue
Block a user