Added release/acquire memory barriers to the atomic API
* Added a destructor to clean up TLS memory at thread shutdown * Refactored the TLS code to have platform independent code and a small platform dependent core with a fallback to generic code if platform dependent functions fail. * Fixed recursion issues with SDL_GetErrBuf()
This commit is contained in:
parent
086ecc9949
commit
557bbf3fe6
10 changed files with 334 additions and 319 deletions
|
@ -45,6 +45,7 @@
|
|||
*
|
||||
* There's also lots of good information here:
|
||||
* http://www.1024cores.net/home/lock-free-algorithms
|
||||
* http://preshing.com/
|
||||
*
|
||||
* These operations may or may not actually be implemented using
|
||||
* processor specific atomic operations. When possible they are
|
||||
|
@ -135,6 +136,32 @@ void _ReadWriteBarrier(void);
|
|||
{ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Memory barriers are designed to prevent reads and writes from being
|
||||
* reordered by the compiler and being seen out of order on multi-core CPUs.
|
||||
*
|
||||
* A typical pattern would be for thread A to write some data and a flag,
|
||||
* and for thread B to read the flag and get the data. In this case you
|
||||
* would insert a release barrier between writing the data and the flag,
|
||||
* guaranteeing that the data write completes no later than the flag is
|
||||
* written, and you would insert an acquire barrier between reading the
|
||||
* flag and reading the data, to ensure that all the reads associated
|
||||
* with the flag have completed.
|
||||
*
|
||||
* In this pattern you should always see a release barrier paired with
|
||||
* an acquire barrier and you should gate the data reads/writes with a
|
||||
* single flag variable.
|
||||
*
|
||||
* For more information on these semantics, take a look at the blog post:
|
||||
* http://preshing.com/20120913/acquire-and-release-semantics
|
||||
*/
|
||||
/* FIXME: This is correct for x86 and x64 but not other CPUs
|
||||
For PPC we need the lwsync instruction, and on ARM some variant of dmb
|
||||
*/
|
||||
#define SDL_MemoryBarrierRelease() SDL_CompilerBarrier()
|
||||
#define SDL_MemoryBarrierAcquire() SDL_CompilerBarrier()
|
||||
|
||||
|
||||
/* Platform specific optimized versions of the atomic functions,
|
||||
* you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
|
||||
*/
|
||||
|
|
|
@ -48,8 +48,8 @@ typedef struct SDL_Thread SDL_Thread;
|
|||
/* The SDL thread ID */
|
||||
typedef unsigned long SDL_threadID;
|
||||
|
||||
/* Thread local storage ID */
|
||||
typedef int SDL_TLSID;
|
||||
/* Thread local storage ID, 0 is the invalid ID */
|
||||
typedef unsigned SDL_TLSID;
|
||||
|
||||
/* The SDL thread priority
|
||||
*
|
||||
|
@ -219,13 +219,14 @@ extern DECLSPEC void * SDLCALL SDL_TLSGet(SDL_TLSID id);
|
|||
*
|
||||
* \param id The thread local storage ID
|
||||
* \param value The value to associate with the ID for the current thread
|
||||
* \param destructor A function called when the thread exits, to free the value.
|
||||
*
|
||||
* \return 0 on success, -1 on error
|
||||
*
|
||||
* \sa SDL_TLSCreate()
|
||||
* \sa SDL_TLSGet()
|
||||
*/
|
||||
extern DECLSPEC int SDLCALL SDL_TLSSet(SDL_TLSID id, const void *value);
|
||||
extern DECLSPEC int SDLCALL SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void*));
|
||||
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
|
|
|
@ -50,6 +50,12 @@ extern int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority);
|
|||
*/
|
||||
extern void SDL_SYS_WaitThread(SDL_Thread * thread);
|
||||
|
||||
/* Get the thread local storage for this thread */
|
||||
extern SDL_TLSData *SDL_SYS_GetTLSData();
|
||||
|
||||
/* Set the thread local storage for this thread */
|
||||
extern int SDL_SYS_SetTLSData(SDL_TLSData *data);
|
||||
|
||||
#endif /* _SDL_systhread_h */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
|
|
@ -28,38 +28,219 @@
|
|||
#include "../SDL_error_c.h"
|
||||
|
||||
|
||||
SDL_TLSID
|
||||
SDL_TLSCreate()
|
||||
{
|
||||
static SDL_atomic_t SDL_tls_id;
|
||||
return SDL_AtomicIncRef(&SDL_tls_id)+1;
|
||||
}
|
||||
|
||||
void *
|
||||
SDL_TLSGet(SDL_TLSID id)
|
||||
{
|
||||
SDL_TLSData *storage;
|
||||
|
||||
storage = SDL_SYS_GetTLSData();
|
||||
if (!storage || id == 0 || id > storage->limit) {
|
||||
return NULL;
|
||||
}
|
||||
return storage->array[id-1].data;
|
||||
}
|
||||
|
||||
int
|
||||
SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void *))
|
||||
{
|
||||
SDL_TLSData *storage;
|
||||
|
||||
if (id == 0) {
|
||||
return SDL_InvalidParamError("id");
|
||||
}
|
||||
|
||||
storage = SDL_SYS_GetTLSData();
|
||||
if (!storage || id > storage->limit) {
|
||||
int i, oldlimit, newlimit;
|
||||
|
||||
oldlimit = storage ? storage->limit : 0;
|
||||
newlimit = (id + TLS_ALLOC_CHUNKSIZE);
|
||||
storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
|
||||
if (!storage) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
storage->limit = newlimit;
|
||||
for (i = oldlimit; i < newlimit; ++i) {
|
||||
storage->array[i].data = NULL;
|
||||
storage->array[i].destructor = NULL;
|
||||
}
|
||||
if (SDL_SYS_SetTLSData(storage) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
storage->array[id-1].data = SDL_const_cast(void*, value);
|
||||
storage->array[id-1].destructor = destructor;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
SDL_TLSCleanup()
|
||||
{
|
||||
SDL_TLSData *storage;
|
||||
|
||||
storage = SDL_SYS_GetTLSData();
|
||||
if (storage) {
|
||||
int i;
|
||||
for (i = 0; i < storage->limit; ++i) {
|
||||
if (storage->array[i].destructor) {
|
||||
storage->array[i].destructor(storage->array[i].data);
|
||||
}
|
||||
}
|
||||
SDL_SYS_SetTLSData(NULL);
|
||||
SDL_free(storage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This is a generic implementation of thread-local storage which doesn't
|
||||
require additional OS support.
|
||||
|
||||
It is not especially efficient and doesn't clean up thread-local storage
|
||||
as threads exit. If there is a real OS that doesn't support thread-local
|
||||
storage this implementation should be improved to be production quality.
|
||||
*/
|
||||
|
||||
typedef struct SDL_TLSEntry {
|
||||
SDL_threadID thread;
|
||||
SDL_TLSData *storage;
|
||||
struct SDL_TLSEntry *next;
|
||||
} SDL_TLSEntry;
|
||||
|
||||
static SDL_mutex *SDL_generic_TLS_mutex;
|
||||
static SDL_TLSEntry *SDL_generic_TLS;
|
||||
|
||||
|
||||
SDL_TLSData *
|
||||
SDL_Generic_GetTLSData()
|
||||
{
|
||||
SDL_threadID thread = SDL_ThreadID();
|
||||
SDL_TLSEntry *entry;
|
||||
SDL_TLSData *storage = NULL;
|
||||
|
||||
if (!SDL_generic_TLS_mutex) {
|
||||
static SDL_SpinLock tls_lock;
|
||||
SDL_AtomicLock(&tls_lock);
|
||||
if (!SDL_generic_TLS_mutex) {
|
||||
SDL_mutex *mutex = SDL_CreateMutex();
|
||||
SDL_MemoryBarrierRelease();
|
||||
SDL_generic_TLS_mutex = mutex;
|
||||
if (!SDL_generic_TLS_mutex) {
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
}
|
||||
|
||||
SDL_MemoryBarrierAcquire();
|
||||
SDL_LockMutex(SDL_generic_TLS_mutex);
|
||||
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
|
||||
if (entry->thread == thread) {
|
||||
storage = entry->storage;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(SDL_generic_TLS_mutex);
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
int
|
||||
SDL_Generic_SetTLSData(SDL_TLSData *storage)
|
||||
{
|
||||
SDL_threadID thread = SDL_ThreadID();
|
||||
SDL_TLSEntry *prev, *entry;
|
||||
|
||||
/* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
|
||||
SDL_LockMutex(SDL_generic_TLS_mutex);
|
||||
prev = NULL;
|
||||
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
|
||||
if (entry->thread == thread) {
|
||||
if (storage) {
|
||||
entry->storage = storage;
|
||||
} else {
|
||||
if (prev) {
|
||||
prev->next = entry->next;
|
||||
} else {
|
||||
SDL_generic_TLS = entry->next;
|
||||
}
|
||||
SDL_free(entry);
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev = entry;
|
||||
}
|
||||
if (!entry) {
|
||||
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
|
||||
if (entry) {
|
||||
entry->thread = thread;
|
||||
entry->storage = storage;
|
||||
entry->next = SDL_generic_TLS;
|
||||
SDL_generic_TLS = entry;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(SDL_generic_TLS_mutex);
|
||||
|
||||
if (!entry) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Routine to get the thread-specific error variable */
|
||||
SDL_error *
|
||||
SDL_GetErrBuf(void)
|
||||
{
|
||||
static SDL_SpinLock spinlock;
|
||||
static SDL_SpinLock tls_lock;
|
||||
static SDL_bool tls_being_created;
|
||||
static SDL_TLSID tls_errbuf;
|
||||
static SDL_error SDL_global_errbuf;
|
||||
const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
|
||||
SDL_error *errbuf;
|
||||
|
||||
/* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
|
||||
It also means it's possible for another thread to also use SDL_global_errbuf,
|
||||
but that's very unlikely and hopefully won't cause issues.
|
||||
*/
|
||||
if (!tls_errbuf && !tls_being_created) {
|
||||
SDL_AtomicLock(&spinlock);
|
||||
SDL_AtomicLock(&tls_lock);
|
||||
if (!tls_errbuf) {
|
||||
/* SDL_TLSCreate() could fail and call SDL_SetError() */
|
||||
SDL_TLSID slot;
|
||||
tls_being_created = SDL_TRUE;
|
||||
tls_errbuf = SDL_TLSCreate();
|
||||
slot = SDL_TLSCreate();
|
||||
tls_being_created = SDL_FALSE;
|
||||
SDL_MemoryBarrierRelease();
|
||||
tls_errbuf = slot;
|
||||
}
|
||||
SDL_AtomicUnlock(&spinlock);
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
}
|
||||
if (!tls_errbuf) {
|
||||
return &SDL_global_errbuf;
|
||||
}
|
||||
|
||||
errbuf = SDL_TLSGet(tls_errbuf);
|
||||
SDL_MemoryBarrierAcquire();
|
||||
errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
|
||||
if (errbuf == ALLOCATION_IN_PROGRESS) {
|
||||
return &SDL_global_errbuf;
|
||||
}
|
||||
if (!errbuf) {
|
||||
/* Mark that we're in the middle of allocating our buffer */
|
||||
SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
|
||||
errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
|
||||
if (!errbuf) {
|
||||
SDL_TLSSet(tls_errbuf, NULL, NULL);
|
||||
return &SDL_global_errbuf;
|
||||
}
|
||||
SDL_zerop(errbuf);
|
||||
SDL_TLSSet(tls_errbuf, errbuf);
|
||||
SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
|
||||
}
|
||||
return errbuf;
|
||||
}
|
||||
|
@ -82,9 +263,7 @@ SDL_RunThread(void *data)
|
|||
void *userdata = args->data;
|
||||
int *statusloc = &args->info->status;
|
||||
|
||||
/* Perform any system-dependent setup
|
||||
- this function cannot fail, and cannot use SDL_SetError()
|
||||
*/
|
||||
/* Perform any system-dependent setup - this function may not fail */
|
||||
SDL_SYS_SetupThread(args->info->name);
|
||||
|
||||
/* Get the thread id */
|
||||
|
@ -95,6 +274,9 @@ SDL_RunThread(void *data)
|
|||
|
||||
/* Run the function */
|
||||
*statusloc = userfunc(userdata);
|
||||
|
||||
/* Clean up thread-local storage */
|
||||
SDL_TLSCleanup();
|
||||
}
|
||||
|
||||
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
|
||||
|
|
|
@ -56,6 +56,30 @@ struct SDL_Thread
|
|||
/* This is the function called to run a thread */
|
||||
extern void SDL_RunThread(void *data);
|
||||
|
||||
/* This is the system-independent thread local storage structure */
|
||||
typedef struct {
|
||||
int limit;
|
||||
struct {
|
||||
void *data;
|
||||
void (*destructor)(void*);
|
||||
} array[1];
|
||||
} SDL_TLSData;
|
||||
|
||||
/* This is how many TLS entries we allocate at once */
|
||||
#define TLS_ALLOC_CHUNKSIZE 4
|
||||
|
||||
/* Get cross-platform, slow, thread local storage for this thread.
|
||||
This is only intended as a fallback if getting real thread-local
|
||||
storage fails or isn't supported on this platform.
|
||||
*/
|
||||
extern SDL_TLSData *SDL_Generic_GetTLSData();
|
||||
|
||||
/* Set cross-platform, slow, thread local storage for this thread.
|
||||
This is only intended as a fallback if getting real thread-local
|
||||
storage fails or isn't supported on this platform.
|
||||
*/
|
||||
extern int SDL_Generic_SetTLSData(SDL_TLSData *data);
|
||||
|
||||
#endif /* _SDL_thread_c_h */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
|
|
@ -21,81 +21,47 @@
|
|||
|
||||
#include "SDL_config.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "../SDL_thread_c.h"
|
||||
|
||||
#if SDL_THREAD_BEOS
|
||||
|
||||
#include <support/TLS.h>
|
||||
|
||||
|
||||
#define TLS_ALLOC_CHUNKSIZE 8
|
||||
|
||||
typedef struct {
|
||||
int limit;
|
||||
void *data[1];
|
||||
} SDL_TLSData;
|
||||
|
||||
static SDL_SpinLock tls_lock;
|
||||
static int32 thread_local_storage = B_NO_MEMORY;
|
||||
static SDL_atomic_t tls_id;
|
||||
static SDL_bool generic_local_storage = SDL_FALSE;
|
||||
|
||||
|
||||
SDL_TLSID
|
||||
SDL_TLSCreate()
|
||||
SDL_TLSData *
|
||||
SDL_SYS_GetTLSData()
|
||||
{
|
||||
if (thread_local_storage == B_NO_MEMORY) {
|
||||
SDL_AtomicLock(&tls_lock);
|
||||
if (thread_local_storage == B_NO_MEMORY) {
|
||||
thread_local_storage = tls_allocate();
|
||||
if (thread_local_storage == B_NO_MEMORY) {
|
||||
SDL_SetError("tls_allocate() failed");
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
return 0;
|
||||
if (thread_local_storage == B_NO_MEMORY && !generic_local_storage) {
|
||||
static SDL_SpinLock lock;
|
||||
SDL_AtomicLock(&lock);
|
||||
if (thread_local_storage == B_NO_MEMORY && !generic_local_storage) {
|
||||
int32 storage = tls_allocate();
|
||||
if (storage != B_NO_MEMORY) {
|
||||
SDL_MemoryBarrierRelease();
|
||||
thread_local_storage = storage;
|
||||
} else {
|
||||
generic_local_storage = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
SDL_AtomicUnlock(&lock);
|
||||
}
|
||||
return SDL_AtomicIncRef(&tls_id)+1;
|
||||
}
|
||||
|
||||
void *
|
||||
SDL_TLSGet(SDL_TLSID id)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
data = (SDL_TLSData *)tls_get(thread_local_storage);
|
||||
if (!data || id <= 0 || id > data->limit) {
|
||||
return NULL;
|
||||
if (generic_local_storage) {
|
||||
return SDL_Generic_GetTLSData();
|
||||
}
|
||||
return data->data[id-1];
|
||||
SDL_MemoryBarrierAcquire();
|
||||
return (SDL_TLSData *)tls_get(thread_local_storage);
|
||||
}
|
||||
|
||||
int
|
||||
SDL_TLSSet(SDL_TLSID id, const void *value)
|
||||
SDL_SYS_SetTLSData(SDL_TLSData *data)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
if (thread_local_storage == B_NO_MEMORY || id <= 0) {
|
||||
return SDL_InvalidParamError(id);
|
||||
if (generic_local_storage) {
|
||||
return SDL_Generic_SetTLSData(data);
|
||||
}
|
||||
|
||||
data = (SDL_TLSData *)tls_get(thread_local_storage);
|
||||
if (!data || id > data->limit) {
|
||||
int i, oldlimit, newlimit;
|
||||
|
||||
oldlimit = data ? data->limit : 0;
|
||||
newlimit = (id + TLS_ALLOC_CHUNKSIZE);
|
||||
data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*));
|
||||
if (!data) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
data->limit = newlimit;
|
||||
for (i = oldlimit; i < newlimit; ++i) {
|
||||
data->data[i] = NULL;
|
||||
}
|
||||
tls_set(thread_local_storage, data);
|
||||
}
|
||||
|
||||
data->data[id-1] = SDL_const_cast(void*, value);
|
||||
tls_set(thread_local_storage, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,144 +20,19 @@
|
|||
*/
|
||||
|
||||
#include "SDL_config.h"
|
||||
#include "SDL_thread.h"
|
||||
|
||||
/* This is a generic implementation of thread-local storage which doesn't
|
||||
require additional OS support.
|
||||
|
||||
It is not especially efficient and doesn't clean up thread-local storage
|
||||
as threads exit. If there is a real OS that doesn't support thread-local
|
||||
storage this implementation should be improved to be production quality.
|
||||
*/
|
||||
|
||||
#define TLS_ALLOC_CHUNKSIZE 8
|
||||
|
||||
typedef struct {
|
||||
int limit;
|
||||
void *data[1];
|
||||
} SDL_TLSData;
|
||||
|
||||
typedef struct SDL_TLSEntry {
|
||||
SDL_threadID thread;
|
||||
SDL_TLSData *data;
|
||||
struct SDL_TLSEntry *next;
|
||||
} SDL_TLSEntry;
|
||||
|
||||
static SDL_SpinLock tls_lock;
|
||||
static SDL_mutex *tls_mutex;
|
||||
static SDL_TLSEntry *thread_local_storage;
|
||||
static SDL_atomic_t tls_id;
|
||||
#include "../SDL_thread_c.h"
|
||||
|
||||
|
||||
static SDL_TLSData *GetTLSData()
|
||||
SDL_TLSData *
|
||||
SDL_SYS_GetTLSData()
|
||||
{
|
||||
SDL_threadID thread = SDL_ThreadID();
|
||||
SDL_TLSEntry *entry;
|
||||
SDL_TLSData *data = NULL;
|
||||
|
||||
if (!tls_mutex) {
|
||||
SDL_AtomicLock(&tls_lock);
|
||||
if (!tls_mutex) {
|
||||
tls_mutex = SDL_CreateMutex();
|
||||
if (!tls_mutex) {
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
}
|
||||
|
||||
SDL_LockMutex(tls_mutex);
|
||||
for (entry = thread_local_storage; entry; entry = entry->next) {
|
||||
if (entry->thread == thread) {
|
||||
data = entry->data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(tls_mutex);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int SetTLSData(SDL_TLSData *data)
|
||||
{
|
||||
SDL_threadID thread = SDL_ThreadID();
|
||||
SDL_TLSEntry *entry;
|
||||
|
||||
/* GetTLSData() is always called first, so we can assume tls_mutex */
|
||||
SDL_LockMutex(tls_mutex);
|
||||
for (entry = thread_local_storage; entry; entry = entry->next) {
|
||||
if (entry->thread == thread) {
|
||||
entry->data = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!entry) {
|
||||
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
|
||||
if (entry) {
|
||||
entry->thread = thread;
|
||||
entry->data = data;
|
||||
entry->next = thread_local_storage;
|
||||
thread_local_storage = entry;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(tls_mutex);
|
||||
|
||||
if (!entry) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
SDL_TLSID
|
||||
SDL_TLSCreate()
|
||||
{
|
||||
return SDL_AtomicIncRef(&tls_id)+1;
|
||||
}
|
||||
|
||||
void *
|
||||
SDL_TLSGet(SDL_TLSID id)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
data = GetTLSData();
|
||||
if (!data || id <= 0 || id > data->limit) {
|
||||
return NULL;
|
||||
}
|
||||
return data->data[id-1];
|
||||
return SDL_Generic_GetTLSData();
|
||||
}
|
||||
|
||||
int
|
||||
SDL_TLSSet(SDL_TLSID id, const void *value)
|
||||
SDL_SYS_SetTLSData(SDL_TLSData *data)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
if (id <= 0) {
|
||||
return SDL_InvalidParamError(id);
|
||||
}
|
||||
|
||||
data = GetTLSData();
|
||||
if (!data || id > data->limit) {
|
||||
int i, oldlimit, newlimit;
|
||||
|
||||
oldlimit = data ? data->limit : 0;
|
||||
newlimit = (id + TLS_ALLOC_CHUNKSIZE);
|
||||
data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*));
|
||||
if (!data) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
data->limit = newlimit;
|
||||
for (i = oldlimit; i < newlimit; ++i) {
|
||||
data->data[i] = NULL;
|
||||
}
|
||||
if (SetTLSData(data) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
data->data[id-1] = SDL_const_cast(void*, value);
|
||||
return 0;
|
||||
return SDL_Generic_SetTLSData(data);
|
||||
}
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
|
|
|
@ -18,83 +18,51 @@
|
|||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "SDL_config.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "../SDL_thread_c.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
#define TLS_ALLOC_CHUNKSIZE 8
|
||||
#define INVALID_PTHREAD_KEY ((pthread_key_t)-1)
|
||||
|
||||
typedef struct {
|
||||
int limit;
|
||||
void *data[1];
|
||||
} SDL_TLSData;
|
||||
static pthread_key_t thread_local_storage = INVALID_PTHREAD_KEY;
|
||||
static SDL_bool generic_local_storage = SDL_FALSE;
|
||||
|
||||
static SDL_SpinLock tls_lock;
|
||||
static pthread_key_t thread_local_storage;
|
||||
static SDL_atomic_t tls_id;
|
||||
|
||||
|
||||
SDL_TLSID
|
||||
SDL_TLSCreate()
|
||||
SDL_TLSData *
|
||||
SDL_SYS_GetTLSData()
|
||||
{
|
||||
if (!thread_local_storage) {
|
||||
SDL_AtomicLock(&tls_lock);
|
||||
if (!thread_local_storage) {
|
||||
if (pthread_key_create(&thread_local_storage, NULL) != 0) {
|
||||
SDL_SetError("pthread_key_create() failed");
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
return 0;
|
||||
if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) {
|
||||
static SDL_SpinLock lock;
|
||||
SDL_AtomicLock(&lock);
|
||||
if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) {
|
||||
pthread_key_t storage;
|
||||
if (pthread_key_create(&storage, NULL) == 0) {
|
||||
SDL_MemoryBarrierRelease();
|
||||
thread_local_storage = storage;
|
||||
} else {
|
||||
generic_local_storage = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
SDL_AtomicUnlock(&lock);
|
||||
}
|
||||
return SDL_AtomicIncRef(&tls_id)+1;
|
||||
}
|
||||
|
||||
void *
|
||||
SDL_TLSGet(SDL_TLSID id)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
data = (SDL_TLSData *)pthread_getspecific(thread_local_storage);
|
||||
if (!data || id <= 0 || id > data->limit) {
|
||||
return NULL;
|
||||
if (generic_local_storage) {
|
||||
return SDL_Generic_GetTLSData();
|
||||
}
|
||||
return data->data[id-1];
|
||||
SDL_MemoryBarrierAcquire();
|
||||
return (SDL_TLSData *)pthread_getspecific(thread_local_storage);
|
||||
}
|
||||
|
||||
int
|
||||
SDL_TLSSet(SDL_TLSID id, const void *value)
|
||||
SDL_SYS_SetTLSData(SDL_TLSData *data)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
if (!thread_local_storage || id <= 0) {
|
||||
return SDL_InvalidParamError(id);
|
||||
if (generic_local_storage) {
|
||||
return SDL_Generic_SetTLSData(data);
|
||||
}
|
||||
|
||||
data = (SDL_TLSData *)pthread_getspecific(thread_local_storage);
|
||||
if (!data || id > data->limit) {
|
||||
int i, oldlimit, newlimit;
|
||||
|
||||
oldlimit = data ? data->limit : 0;
|
||||
newlimit = (id + TLS_ALLOC_CHUNKSIZE);
|
||||
data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*));
|
||||
if (!data) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
data->limit = newlimit;
|
||||
for (i = oldlimit; i < newlimit; ++i) {
|
||||
data->data[i] = NULL;
|
||||
}
|
||||
if (pthread_setspecific(thread_local_storage, data) != 0) {
|
||||
return SDL_SetError("pthread_setspecific() failed");
|
||||
}
|
||||
if (pthread_setspecific(thread_local_storage, data) != 0) {
|
||||
return SDL_SetError("pthread_setspecific() failed");
|
||||
}
|
||||
|
||||
data->data[id-1] = SDL_const_cast(void*, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,83 +21,49 @@
|
|||
|
||||
#include "SDL_config.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "../SDL_thread_c.h"
|
||||
|
||||
#if SDL_THREAD_WINDOWS
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
|
||||
|
||||
#define TLS_ALLOC_CHUNKSIZE 8
|
||||
|
||||
typedef struct {
|
||||
int limit;
|
||||
void *data[1];
|
||||
} SDL_TLSData;
|
||||
|
||||
static SDL_SpinLock tls_lock;
|
||||
static DWORD thread_local_storage = TLS_OUT_OF_INDEXES;
|
||||
static SDL_atomic_t tls_id;
|
||||
static SDL_bool generic_local_storage = SDL_FALSE;
|
||||
|
||||
|
||||
SDL_TLSID
|
||||
SDL_TLSCreate()
|
||||
SDL_TLSData *
|
||||
SDL_SYS_GetTLSData()
|
||||
{
|
||||
if (thread_local_storage == TLS_OUT_OF_INDEXES) {
|
||||
SDL_AtomicLock(&tls_lock);
|
||||
if (thread_local_storage == TLS_OUT_OF_INDEXES) {
|
||||
thread_local_storage = TlsAlloc();
|
||||
if (thread_local_storage == TLS_OUT_OF_INDEXES) {
|
||||
SDL_SetError("TlsAlloc() failed");
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
return 0;
|
||||
if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) {
|
||||
static SDL_SpinLock lock;
|
||||
SDL_AtomicLock(&lock);
|
||||
if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) {
|
||||
DWORD storage = TlsAlloc();
|
||||
if (storage != TLS_OUT_OF_INDEXES) {
|
||||
SDL_MemoryBarrierRelease();
|
||||
thread_local_storage = storage;
|
||||
} else {
|
||||
generic_local_storage = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
SDL_AtomicUnlock(&tls_lock);
|
||||
SDL_AtomicUnlock(&lock);
|
||||
}
|
||||
return SDL_AtomicIncRef(&tls_id)+1;
|
||||
}
|
||||
|
||||
void *
|
||||
SDL_TLSGet(SDL_TLSID id)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
data = (SDL_TLSData *)TlsGetValue(thread_local_storage);
|
||||
if (!data || id <= 0 || id > data->limit) {
|
||||
return NULL;
|
||||
if (generic_local_storage) {
|
||||
return SDL_Generic_GetTLSData();
|
||||
}
|
||||
return data->data[id-1];
|
||||
SDL_MemoryBarrierAcquire();
|
||||
return (SDL_TLSData *)TlsGetValue(thread_local_storage);
|
||||
}
|
||||
|
||||
int
|
||||
SDL_TLSSet(SDL_TLSID id, const void *value)
|
||||
SDL_SYS_SetTLSData(SDL_TLSData *data)
|
||||
{
|
||||
SDL_TLSData *data;
|
||||
|
||||
if (thread_local_storage == TLS_OUT_OF_INDEXES || id <= 0) {
|
||||
return SDL_InvalidParamError(id);
|
||||
if (generic_local_storage) {
|
||||
return SDL_Generic_SetTLSData(data);
|
||||
}
|
||||
|
||||
data = (SDL_TLSData *)TlsGetValue(thread_local_storage);
|
||||
if (!data || id > data->limit) {
|
||||
int i, oldlimit, newlimit;
|
||||
|
||||
oldlimit = data ? data->limit : 0;
|
||||
newlimit = (id + TLS_ALLOC_CHUNKSIZE);
|
||||
data = (SDL_TLSData *)SDL_realloc(data, sizeof(*data)+(newlimit-1)*sizeof(void*));
|
||||
if (!data) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
data->limit = newlimit;
|
||||
for (i = oldlimit; i < newlimit; ++i) {
|
||||
data->data[i] = NULL;
|
||||
}
|
||||
if (!TlsSetValue(thread_local_storage, data)) {
|
||||
return SDL_SetError("TlsSetValue() failed");
|
||||
}
|
||||
if (!TlsSetValue(thread_local_storage, data)) {
|
||||
return SDL_SetError("TlsSetValue() failed");
|
||||
}
|
||||
|
||||
data->data[id-1] = SDL_const_cast(void*, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ quit(int rc)
|
|||
int SDLCALL
|
||||
ThreadFunc(void *data)
|
||||
{
|
||||
SDL_TLSSet(tls, "baby thread");
|
||||
SDL_TLSSet(tls, "baby thread", NULL);
|
||||
printf("Started thread %s: My thread id is %lu, thread data = %s\n",
|
||||
(char *) data, SDL_ThreadID(), (const char *)SDL_TLSGet(tls));
|
||||
while (alive) {
|
||||
|
@ -66,7 +66,7 @@ main(int argc, char *argv[])
|
|||
|
||||
tls = SDL_TLSCreate();
|
||||
SDL_assert(tls);
|
||||
SDL_TLSSet(tls, "main thread");
|
||||
SDL_TLSSet(tls, "main thread", NULL);
|
||||
printf("Main thread data initially: %s\n", (const char *)SDL_TLSGet(tls));
|
||||
|
||||
alive = 1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue