src/thread/SDL_thread.c
changeset 7393 358696c354a8
parent 7391 a29895dc5e9a
child 7482 249d8ecbbb7d
     1.1 --- a/src/thread/SDL_thread.c	Wed Jul 10 02:37:57 2013 -0700
     1.2 +++ b/src/thread/SDL_thread.c	Wed Jul 10 18:31:17 2013 -0700
     1.3 @@ -28,38 +28,219 @@
     1.4  #include "../SDL_error_c.h"
     1.5  
     1.6  
     1.7 +SDL_TLSID
     1.8 +SDL_TLSCreate()
     1.9 +{
    1.10 +    static SDL_atomic_t SDL_tls_id;
    1.11 +    return SDL_AtomicIncRef(&SDL_tls_id)+1;
    1.12 +}
    1.13 +
    1.14 +void *
    1.15 +SDL_TLSGet(SDL_TLSID id)
    1.16 +{
    1.17 +    SDL_TLSData *storage;
    1.18 +
    1.19 +    storage = SDL_SYS_GetTLSData();
    1.20 +    if (!storage || id == 0 || id > storage->limit) {
    1.21 +        return NULL;
    1.22 +    }
    1.23 +    return storage->array[id-1].data;
    1.24 +}
    1.25 +
    1.26 +int
    1.27 +SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void *))
    1.28 +{
    1.29 +    SDL_TLSData *storage;
    1.30 +
    1.31 +    if (id == 0) {
    1.32 +        return SDL_InvalidParamError("id");
    1.33 +    }
    1.34 +
    1.35 +    storage = SDL_SYS_GetTLSData();
    1.36 +    if (!storage || id > storage->limit) {
    1.37 +        int i, oldlimit, newlimit;
    1.38 +
    1.39 +        oldlimit = storage ? storage->limit : 0;
    1.40 +        newlimit = (id + TLS_ALLOC_CHUNKSIZE);
    1.41 +        storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
    1.42 +        if (!storage) {
    1.43 +            return SDL_OutOfMemory();
    1.44 +        }
    1.45 +        storage->limit = newlimit;
    1.46 +        for (i = oldlimit; i < newlimit; ++i) {
    1.47 +            storage->array[i].data = NULL;
    1.48 +            storage->array[i].destructor = NULL;
    1.49 +        }
    1.50 +        if (SDL_SYS_SetTLSData(storage) != 0) {
    1.51 +            return -1;
    1.52 +        }
    1.53 +    }
    1.54 +
    1.55 +    storage->array[id-1].data = SDL_const_cast(void*, value);
    1.56 +    storage->array[id-1].destructor = destructor;
    1.57 +    return 0;
    1.58 +}
    1.59 +
    1.60 +static void
    1.61 +SDL_TLSCleanup()
    1.62 +{
    1.63 +    SDL_TLSData *storage;
    1.64 +
    1.65 +    storage = SDL_SYS_GetTLSData();
    1.66 +    if (storage) {
    1.67 +        int i;
    1.68 +        for (i = 0; i < storage->limit; ++i) {
    1.69 +            if (storage->array[i].destructor) {
    1.70 +                storage->array[i].destructor(storage->array[i].data);
    1.71 +            }
    1.72 +        }
    1.73 +        SDL_SYS_SetTLSData(NULL);
    1.74 +        SDL_free(storage);
    1.75 +    }
    1.76 +}
    1.77 +
    1.78 +
    1.79 +/* This is a generic implementation of thread-local storage which doesn't
    1.80 +   require additional OS support.
    1.81 +
    1.82 +   It is not especially efficient and doesn't clean up thread-local storage
    1.83 +   as threads exit.  If there is a real OS that doesn't support thread-local
    1.84 +   storage this implementation should be improved to be production quality.
    1.85 +*/
    1.86 +
    1.87 +typedef struct SDL_TLSEntry {
    1.88 +    SDL_threadID thread;
    1.89 +    SDL_TLSData *storage;
    1.90 +    struct SDL_TLSEntry *next;
    1.91 +} SDL_TLSEntry;
    1.92 +
    1.93 +static SDL_mutex *SDL_generic_TLS_mutex;
    1.94 +static SDL_TLSEntry *SDL_generic_TLS;
    1.95 +
    1.96 +
    1.97 +SDL_TLSData *
    1.98 +SDL_Generic_GetTLSData()
    1.99 +{
   1.100 +    SDL_threadID thread = SDL_ThreadID();
   1.101 +    SDL_TLSEntry *entry;
   1.102 +    SDL_TLSData *storage = NULL;
   1.103 +
   1.104 +    if (!SDL_generic_TLS_mutex) {
   1.105 +        static SDL_SpinLock tls_lock;
   1.106 +        SDL_AtomicLock(&tls_lock);
   1.107 +        if (!SDL_generic_TLS_mutex) {
   1.108 +            SDL_mutex *mutex = SDL_CreateMutex();
   1.109 +            SDL_MemoryBarrierRelease();
   1.110 +            SDL_generic_TLS_mutex = mutex;
   1.111 +            if (!SDL_generic_TLS_mutex) {
   1.112 +                SDL_AtomicUnlock(&tls_lock);
   1.113 +                return NULL;
   1.114 +            }
   1.115 +        }
   1.116 +        SDL_AtomicUnlock(&tls_lock);
   1.117 +    }
   1.118 +
   1.119 +    SDL_MemoryBarrierAcquire();
   1.120 +    SDL_LockMutex(SDL_generic_TLS_mutex);
   1.121 +    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
   1.122 +        if (entry->thread == thread) {
   1.123 +            storage = entry->storage;
   1.124 +            break;
   1.125 +        }
   1.126 +    }
   1.127 +    SDL_UnlockMutex(SDL_generic_TLS_mutex);
   1.128 +
   1.129 +    return storage;
   1.130 +}
   1.131 +
   1.132 +int
   1.133 +SDL_Generic_SetTLSData(SDL_TLSData *storage)
   1.134 +{
   1.135 +    SDL_threadID thread = SDL_ThreadID();
   1.136 +    SDL_TLSEntry *prev, *entry;
   1.137 +
   1.138 +    /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
   1.139 +    SDL_LockMutex(SDL_generic_TLS_mutex);
   1.140 +    prev = NULL;
   1.141 +    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
   1.142 +        if (entry->thread == thread) {
   1.143 +            if (storage) {
   1.144 +                entry->storage = storage;
   1.145 +            } else {
   1.146 +                if (prev) {
   1.147 +                    prev->next = entry->next;
   1.148 +                } else {
   1.149 +                    SDL_generic_TLS = entry->next;
   1.150 +                }
   1.151 +                SDL_free(entry);
   1.152 +            }
   1.153 +            break;
   1.154 +        }
   1.155 +        prev = entry;
   1.156 +    }
   1.157 +    if (!entry) {
   1.158 +        entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
   1.159 +        if (entry) {
   1.160 +            entry->thread = thread;
   1.161 +            entry->storage = storage;
   1.162 +            entry->next = SDL_generic_TLS;
   1.163 +            SDL_generic_TLS = entry;
   1.164 +        }
   1.165 +    }
   1.166 +    SDL_UnlockMutex(SDL_generic_TLS_mutex);
   1.167 +
   1.168 +    if (!entry) {
   1.169 +        return SDL_OutOfMemory();
   1.170 +    }
   1.171 +    return 0;
   1.172 +}
   1.173 +
   1.174  /* Routine to get the thread-specific error variable */
   1.175  SDL_error *
   1.176  SDL_GetErrBuf(void)
   1.177  {
   1.178 -    static SDL_SpinLock spinlock;
   1.179 +    static SDL_SpinLock tls_lock;
   1.180      static SDL_bool tls_being_created;
   1.181      static SDL_TLSID tls_errbuf;
   1.182      static SDL_error SDL_global_errbuf;
   1.183 +    const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
   1.184      SDL_error *errbuf;
   1.185  
   1.186 +    /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
   1.187 +       It also means it's possible for another thread to also use SDL_global_errbuf,
   1.188 +       but that's very unlikely and hopefully won't cause issues.
   1.189 +     */
   1.190      if (!tls_errbuf && !tls_being_created) {
   1.191 -        SDL_AtomicLock(&spinlock);
   1.192 +        SDL_AtomicLock(&tls_lock);
   1.193          if (!tls_errbuf) {
   1.194 -            /* SDL_TLSCreate() could fail and call SDL_SetError() */
   1.195 +            SDL_TLSID slot;
   1.196              tls_being_created = SDL_TRUE;
   1.197 -            tls_errbuf = SDL_TLSCreate();
   1.198 +            slot = SDL_TLSCreate();
   1.199              tls_being_created = SDL_FALSE;
   1.200 +            SDL_MemoryBarrierRelease();
   1.201 +            tls_errbuf = slot;
   1.202          }
   1.203 -        SDL_AtomicUnlock(&spinlock);
   1.204 +        SDL_AtomicUnlock(&tls_lock);
   1.205      }
   1.206      if (!tls_errbuf) {
   1.207          return &SDL_global_errbuf;
   1.208      }
   1.209  
   1.210 -    errbuf = SDL_TLSGet(tls_errbuf);
   1.211 +    SDL_MemoryBarrierAcquire();
   1.212 +    errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
   1.213 +    if (errbuf == ALLOCATION_IN_PROGRESS) {
   1.214 +        return &SDL_global_errbuf;
   1.215 +    }
   1.216      if (!errbuf) {
   1.217 +        /* Mark that we're in the middle of allocating our buffer */
   1.218 +        SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
   1.219          errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
   1.220          if (!errbuf) {
   1.221 +            SDL_TLSSet(tls_errbuf, NULL, NULL);
   1.222              return &SDL_global_errbuf;
   1.223          }
   1.224          SDL_zerop(errbuf);
   1.225 -        SDL_TLSSet(tls_errbuf, errbuf);
   1.226 +        SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
   1.227      }
   1.228      return errbuf;
   1.229  }
   1.230 @@ -82,9 +263,7 @@
   1.231      void *userdata = args->data;
   1.232      int *statusloc = &args->info->status;
   1.233  
   1.234 -    /* Perform any system-dependent setup
   1.235 -       - this function cannot fail, and cannot use SDL_SetError()
   1.236 -     */
   1.237 +    /* Perform any system-dependent setup - this function may not fail */
   1.238      SDL_SYS_SetupThread(args->info->name);
   1.239  
   1.240      /* Get the thread id */
   1.241 @@ -95,6 +274,9 @@
   1.242  
   1.243      /* Run the function */
   1.244      *statusloc = userfunc(userdata);
   1.245 +
   1.246 +    /* Clean up thread-local storage */
   1.247 +    SDL_TLSCleanup();
   1.248  }
   1.249  
   1.250  #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD