src/thread/SDL_thread.c
changeset 7393 358696c354a8
parent 7391 a29895dc5e9a
child 7482 249d8ecbbb7d
equal deleted inserted replaced
7392:7e32fcb41b44 7393:358696c354a8
    26 #include "SDL_thread_c.h"
    26 #include "SDL_thread_c.h"
    27 #include "SDL_systhread.h"
    27 #include "SDL_systhread.h"
    28 #include "../SDL_error_c.h"
    28 #include "../SDL_error_c.h"
    29 
    29 
    30 
    30 
       
    31 SDL_TLSID
       
    32 SDL_TLSCreate()
       
    33 {
       
    34     static SDL_atomic_t SDL_tls_id;
       
    35     return SDL_AtomicIncRef(&SDL_tls_id)+1;
       
    36 }
       
    37 
       
    38 void *
       
    39 SDL_TLSGet(SDL_TLSID id)
       
    40 {
       
    41     SDL_TLSData *storage;
       
    42 
       
    43     storage = SDL_SYS_GetTLSData();
       
    44     if (!storage || id == 0 || id > storage->limit) {
       
    45         return NULL;
       
    46     }
       
    47     return storage->array[id-1].data;
       
    48 }
       
    49 
       
    50 int
       
    51 SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void *))
       
    52 {
       
    53     SDL_TLSData *storage;
       
    54 
       
    55     if (id == 0) {
       
    56         return SDL_InvalidParamError("id");
       
    57     }
       
    58 
       
    59     storage = SDL_SYS_GetTLSData();
       
    60     if (!storage || id > storage->limit) {
       
    61         int i, oldlimit, newlimit;
       
    62 
       
    63         oldlimit = storage ? storage->limit : 0;
       
    64         newlimit = (id + TLS_ALLOC_CHUNKSIZE);
       
    65         storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
       
    66         if (!storage) {
       
    67             return SDL_OutOfMemory();
       
    68         }
       
    69         storage->limit = newlimit;
       
    70         for (i = oldlimit; i < newlimit; ++i) {
       
    71             storage->array[i].data = NULL;
       
    72             storage->array[i].destructor = NULL;
       
    73         }
       
    74         if (SDL_SYS_SetTLSData(storage) != 0) {
       
    75             return -1;
       
    76         }
       
    77     }
       
    78 
       
    79     storage->array[id-1].data = SDL_const_cast(void*, value);
       
    80     storage->array[id-1].destructor = destructor;
       
    81     return 0;
       
    82 }
       
    83 
       
    84 static void
       
    85 SDL_TLSCleanup()
       
    86 {
       
    87     SDL_TLSData *storage;
       
    88 
       
    89     storage = SDL_SYS_GetTLSData();
       
    90     if (storage) {
       
    91         int i;
       
    92         for (i = 0; i < storage->limit; ++i) {
       
    93             if (storage->array[i].destructor) {
       
    94                 storage->array[i].destructor(storage->array[i].data);
       
    95             }
       
    96         }
       
    97         SDL_SYS_SetTLSData(NULL);
       
    98         SDL_free(storage);
       
    99     }
       
   100 }
       
   101 
       
   102 
       
   103 /* This is a generic implementation of thread-local storage which doesn't
       
   104    require additional OS support.
       
   105 
       
   106    It is not especially efficient and doesn't clean up thread-local storage
       
   107    as threads exit.  If there is a real OS that doesn't support thread-local
       
   108    storage this implementation should be improved to be production quality.
       
   109 */
       
   110 
       
   111 typedef struct SDL_TLSEntry {
       
   112     SDL_threadID thread;
       
   113     SDL_TLSData *storage;
       
   114     struct SDL_TLSEntry *next;
       
   115 } SDL_TLSEntry;
       
   116 
       
   117 static SDL_mutex *SDL_generic_TLS_mutex;
       
   118 static SDL_TLSEntry *SDL_generic_TLS;
       
   119 
       
   120 
       
   121 SDL_TLSData *
       
   122 SDL_Generic_GetTLSData()
       
   123 {
       
   124     SDL_threadID thread = SDL_ThreadID();
       
   125     SDL_TLSEntry *entry;
       
   126     SDL_TLSData *storage = NULL;
       
   127 
       
   128     if (!SDL_generic_TLS_mutex) {
       
   129         static SDL_SpinLock tls_lock;
       
   130         SDL_AtomicLock(&tls_lock);
       
   131         if (!SDL_generic_TLS_mutex) {
       
   132             SDL_mutex *mutex = SDL_CreateMutex();
       
   133             SDL_MemoryBarrierRelease();
       
   134             SDL_generic_TLS_mutex = mutex;
       
   135             if (!SDL_generic_TLS_mutex) {
       
   136                 SDL_AtomicUnlock(&tls_lock);
       
   137                 return NULL;
       
   138             }
       
   139         }
       
   140         SDL_AtomicUnlock(&tls_lock);
       
   141     }
       
   142 
       
   143     SDL_MemoryBarrierAcquire();
       
   144     SDL_LockMutex(SDL_generic_TLS_mutex);
       
   145     for (entry = SDL_generic_TLS; entry; entry = entry->next) {
       
   146         if (entry->thread == thread) {
       
   147             storage = entry->storage;
       
   148             break;
       
   149         }
       
   150     }
       
   151     SDL_UnlockMutex(SDL_generic_TLS_mutex);
       
   152 
       
   153     return storage;
       
   154 }
       
   155 
       
   156 int
       
   157 SDL_Generic_SetTLSData(SDL_TLSData *storage)
       
   158 {
       
   159     SDL_threadID thread = SDL_ThreadID();
       
   160     SDL_TLSEntry *prev, *entry;
       
   161 
       
   162     /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
       
   163     SDL_LockMutex(SDL_generic_TLS_mutex);
       
   164     prev = NULL;
       
   165     for (entry = SDL_generic_TLS; entry; entry = entry->next) {
       
   166         if (entry->thread == thread) {
       
   167             if (storage) {
       
   168                 entry->storage = storage;
       
   169             } else {
       
   170                 if (prev) {
       
   171                     prev->next = entry->next;
       
   172                 } else {
       
   173                     SDL_generic_TLS = entry->next;
       
   174                 }
       
   175                 SDL_free(entry);
       
   176             }
       
   177             break;
       
   178         }
       
   179         prev = entry;
       
   180     }
       
   181     if (!entry) {
       
   182         entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
       
   183         if (entry) {
       
   184             entry->thread = thread;
       
   185             entry->storage = storage;
       
   186             entry->next = SDL_generic_TLS;
       
   187             SDL_generic_TLS = entry;
       
   188         }
       
   189     }
       
   190     SDL_UnlockMutex(SDL_generic_TLS_mutex);
       
   191 
       
   192     if (!entry) {
       
   193         return SDL_OutOfMemory();
       
   194     }
       
   195     return 0;
       
   196 }
       
   197 
    31 /* Routine to get the thread-specific error variable */
   198 /* Routine to get the thread-specific error variable */
    32 SDL_error *
   199 SDL_error *
    33 SDL_GetErrBuf(void)
   200 SDL_GetErrBuf(void)
    34 {
   201 {
    35     static SDL_SpinLock spinlock;
   202     static SDL_SpinLock tls_lock;
    36     static SDL_bool tls_being_created;
   203     static SDL_bool tls_being_created;
    37     static SDL_TLSID tls_errbuf;
   204     static SDL_TLSID tls_errbuf;
    38     static SDL_error SDL_global_errbuf;
   205     static SDL_error SDL_global_errbuf;
       
   206     const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
    39     SDL_error *errbuf;
   207     SDL_error *errbuf;
    40 
   208 
       
   209     /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
       
   210        It also means it's possible for another thread to also use SDL_global_errbuf,
       
   211        but that's very unlikely and hopefully won't cause issues.
       
   212      */
    41     if (!tls_errbuf && !tls_being_created) {
   213     if (!tls_errbuf && !tls_being_created) {
    42         SDL_AtomicLock(&spinlock);
   214         SDL_AtomicLock(&tls_lock);
    43         if (!tls_errbuf) {
   215         if (!tls_errbuf) {
    44             /* SDL_TLSCreate() could fail and call SDL_SetError() */
   216             SDL_TLSID slot;
    45             tls_being_created = SDL_TRUE;
   217             tls_being_created = SDL_TRUE;
    46             tls_errbuf = SDL_TLSCreate();
   218             slot = SDL_TLSCreate();
    47             tls_being_created = SDL_FALSE;
   219             tls_being_created = SDL_FALSE;
    48         }
   220             SDL_MemoryBarrierRelease();
    49         SDL_AtomicUnlock(&spinlock);
   221             tls_errbuf = slot;
       
   222         }
       
   223         SDL_AtomicUnlock(&tls_lock);
    50     }
   224     }
    51     if (!tls_errbuf) {
   225     if (!tls_errbuf) {
    52         return &SDL_global_errbuf;
   226         return &SDL_global_errbuf;
    53     }
   227     }
    54 
   228 
    55     errbuf = SDL_TLSGet(tls_errbuf);
   229     SDL_MemoryBarrierAcquire();
       
   230     errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
       
   231     if (errbuf == ALLOCATION_IN_PROGRESS) {
       
   232         return &SDL_global_errbuf;
       
   233     }
    56     if (!errbuf) {
   234     if (!errbuf) {
       
   235         /* Mark that we're in the middle of allocating our buffer */
       
   236         SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
    57         errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
   237         errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
    58         if (!errbuf) {
   238         if (!errbuf) {
       
   239             SDL_TLSSet(tls_errbuf, NULL, NULL);
    59             return &SDL_global_errbuf;
   240             return &SDL_global_errbuf;
    60         }
   241         }
    61         SDL_zerop(errbuf);
   242         SDL_zerop(errbuf);
    62         SDL_TLSSet(tls_errbuf, errbuf);
   243         SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
    63     }
   244     }
    64     return errbuf;
   245     return errbuf;
    65 }
   246 }
    66 
   247 
    67 
   248 
    80     thread_args *args = (thread_args *) data;
   261     thread_args *args = (thread_args *) data;
    81     int (SDLCALL * userfunc) (void *) = args->func;
   262     int (SDLCALL * userfunc) (void *) = args->func;
    82     void *userdata = args->data;
   263     void *userdata = args->data;
    83     int *statusloc = &args->info->status;
   264     int *statusloc = &args->info->status;
    84 
   265 
    85     /* Perform any system-dependent setup
   266     /* Perform any system-dependent setup - this function may not fail */
    86        - this function cannot fail, and cannot use SDL_SetError()
       
    87      */
       
    88     SDL_SYS_SetupThread(args->info->name);
   267     SDL_SYS_SetupThread(args->info->name);
    89 
   268 
    90     /* Get the thread id */
   269     /* Get the thread id */
    91     args->info->threadid = SDL_ThreadID();
   270     args->info->threadid = SDL_ThreadID();
    92 
   271 
    93     /* Wake up the parent thread */
   272     /* Wake up the parent thread */
    94     SDL_SemPost(args->wait);
   273     SDL_SemPost(args->wait);
    95 
   274 
    96     /* Run the function */
   275     /* Run the function */
    97     *statusloc = userfunc(userdata);
   276     *statusloc = userfunc(userdata);
       
   277 
       
   278     /* Clean up thread-local storage */
       
   279     SDL_TLSCleanup();
    98 }
   280 }
    99 
   281 
   100 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   282 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   101 #undef SDL_CreateThread
   283 #undef SDL_CreateThread
   102 DECLSPEC SDL_Thread *SDLCALL
   284 DECLSPEC SDL_Thread *SDLCALL