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:
Sam Lantinga 2013-07-10 18:31:17 -07:00
parent 086ecc9949
commit 557bbf3fe6
10 changed files with 334 additions and 319 deletions

View file

@ -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;
}