src/thread/SDL_thread.c
author Sam Lantinga
Thu, 05 Sep 2013 07:15:26 -0700
changeset 7730 e928464b98ec
parent 7606 bb2aa25cca1c
child 7978 70ac84e49797
permissions -rw-r--r--
Fixed bug 2076 - OpenGL doesn't work with --disable-threads

stepik-777

Thread local storage is used to store current window and current opengl context. OpenGL worked before this changeset: 7596 (45e5c263c096)
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* System independent thread management routines for SDL */
    24 
    25 #include "SDL_thread.h"
    26 #include "SDL_thread_c.h"
    27 #include "SDL_systhread.h"
    28 #include "../SDL_error_c.h"
    29 
    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         unsigned 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         unsigned 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_THREADS_DISABLED
   129     if (!SDL_generic_TLS_mutex) {
   130         static SDL_SpinLock tls_lock;
   131         SDL_AtomicLock(&tls_lock);
   132         if (!SDL_generic_TLS_mutex) {
   133             SDL_mutex *mutex = SDL_CreateMutex();
   134             SDL_MemoryBarrierRelease();
   135             SDL_generic_TLS_mutex = mutex;
   136             if (!SDL_generic_TLS_mutex) {
   137                 SDL_AtomicUnlock(&tls_lock);
   138                 return NULL;
   139             }
   140         }
   141         SDL_AtomicUnlock(&tls_lock);
   142     }
   143 #endif /* SDL_THREADS_DISABLED */
   144 
   145     SDL_MemoryBarrierAcquire();
   146     SDL_LockMutex(SDL_generic_TLS_mutex);
   147     for (entry = SDL_generic_TLS; entry; entry = entry->next) {
   148         if (entry->thread == thread) {
   149             storage = entry->storage;
   150             break;
   151         }
   152     }
   153 #if !SDL_THREADS_DISABLED
   154     SDL_UnlockMutex(SDL_generic_TLS_mutex);
   155 #endif
   156 
   157     return storage;
   158 }
   159 
   160 int
   161 SDL_Generic_SetTLSData(SDL_TLSData *storage)
   162 {
   163     SDL_threadID thread = SDL_ThreadID();
   164     SDL_TLSEntry *prev, *entry;
   165 
   166     /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
   167     SDL_LockMutex(SDL_generic_TLS_mutex);
   168     prev = NULL;
   169     for (entry = SDL_generic_TLS; entry; entry = entry->next) {
   170         if (entry->thread == thread) {
   171             if (storage) {
   172                 entry->storage = storage;
   173             } else {
   174                 if (prev) {
   175                     prev->next = entry->next;
   176                 } else {
   177                     SDL_generic_TLS = entry->next;
   178                 }
   179                 SDL_free(entry);
   180             }
   181             break;
   182         }
   183         prev = entry;
   184     }
   185     if (!entry) {
   186         entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
   187         if (entry) {
   188             entry->thread = thread;
   189             entry->storage = storage;
   190             entry->next = SDL_generic_TLS;
   191             SDL_generic_TLS = entry;
   192         }
   193     }
   194     SDL_UnlockMutex(SDL_generic_TLS_mutex);
   195 
   196     if (!entry) {
   197         return SDL_OutOfMemory();
   198     }
   199     return 0;
   200 }
   201 
   202 /* Routine to get the thread-specific error variable */
   203 SDL_error *
   204 SDL_GetErrBuf(void)
   205 {
   206     static SDL_SpinLock tls_lock;
   207     static SDL_bool tls_being_created;
   208     static SDL_TLSID tls_errbuf;
   209     static SDL_error SDL_global_errbuf;
   210     const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
   211     SDL_error *errbuf;
   212 
   213     /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
   214        It also means it's possible for another thread to also use SDL_global_errbuf,
   215        but that's very unlikely and hopefully won't cause issues.
   216      */
   217     if (!tls_errbuf && !tls_being_created) {
   218         SDL_AtomicLock(&tls_lock);
   219         if (!tls_errbuf) {
   220             SDL_TLSID slot;
   221             tls_being_created = SDL_TRUE;
   222             slot = SDL_TLSCreate();
   223             tls_being_created = SDL_FALSE;
   224             SDL_MemoryBarrierRelease();
   225             tls_errbuf = slot;
   226         }
   227         SDL_AtomicUnlock(&tls_lock);
   228     }
   229     if (!tls_errbuf) {
   230         return &SDL_global_errbuf;
   231     }
   232 
   233     SDL_MemoryBarrierAcquire();
   234     errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
   235     if (errbuf == ALLOCATION_IN_PROGRESS) {
   236         return &SDL_global_errbuf;
   237     }
   238     if (!errbuf) {
   239         /* Mark that we're in the middle of allocating our buffer */
   240         SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
   241         errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
   242         if (!errbuf) {
   243             SDL_TLSSet(tls_errbuf, NULL, NULL);
   244             return &SDL_global_errbuf;
   245         }
   246         SDL_zerop(errbuf);
   247         SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
   248     }
   249     return errbuf;
   250 }
   251 
   252 
   253 /* Arguments and callback to setup and run the user thread function */
   254 typedef struct
   255 {
   256     int (SDLCALL * func) (void *);
   257     void *data;
   258     SDL_Thread *info;
   259     SDL_sem *wait;
   260 } thread_args;
   261 
   262 void
   263 SDL_RunThread(void *data)
   264 {
   265     thread_args *args = (thread_args *) data;
   266     int (SDLCALL * userfunc) (void *) = args->func;
   267     void *userdata = args->data;
   268     int *statusloc = &args->info->status;
   269 
   270     /* Perform any system-dependent setup - this function may not fail */
   271     SDL_SYS_SetupThread(args->info->name);
   272 
   273     /* Get the thread id */
   274     args->info->threadid = SDL_ThreadID();
   275 
   276     /* Wake up the parent thread */
   277     SDL_SemPost(args->wait);
   278 
   279     /* Run the function */
   280     *statusloc = userfunc(userdata);
   281 
   282     /* Clean up thread-local storage */
   283     SDL_TLSCleanup();
   284 }
   285 
   286 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   287 #undef SDL_CreateThread
   288 DECLSPEC SDL_Thread *SDLCALL
   289 SDL_CreateThread(int (SDLCALL * fn) (void *),
   290                  const char *name, void *data,
   291                  pfnSDL_CurrentBeginThread pfnBeginThread,
   292                  pfnSDL_CurrentEndThread pfnEndThread)
   293 #else
   294 DECLSPEC SDL_Thread *SDLCALL
   295 SDL_CreateThread(int (SDLCALL * fn) (void *),
   296                  const char *name, void *data)
   297 #endif
   298 {
   299     SDL_Thread *thread;
   300     thread_args *args;
   301     int ret;
   302 
   303     /* Allocate memory for the thread info structure */
   304     thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
   305     if (thread == NULL) {
   306         SDL_OutOfMemory();
   307         return (NULL);
   308     }
   309     SDL_memset(thread, 0, (sizeof *thread));
   310     thread->status = -1;
   311 
   312     /* Set up the arguments for the thread */
   313     if (name != NULL) {
   314         thread->name = SDL_strdup(name);
   315         if (thread->name == NULL) {
   316             SDL_OutOfMemory();
   317             SDL_free(thread);
   318             return (NULL);
   319         }
   320     }
   321 
   322     /* Set up the arguments for the thread */
   323     args = (thread_args *) SDL_malloc(sizeof(*args));
   324     if (args == NULL) {
   325         SDL_OutOfMemory();
   326         if (thread->name) {
   327             SDL_free(thread->name);
   328         }
   329         SDL_free(thread);
   330         return (NULL);
   331     }
   332     args->func = fn;
   333     args->data = data;
   334     args->info = thread;
   335     args->wait = SDL_CreateSemaphore(0);
   336     if (args->wait == NULL) {
   337         if (thread->name) {
   338             SDL_free(thread->name);
   339         }
   340         SDL_free(thread);
   341         SDL_free(args);
   342         return (NULL);
   343     }
   344 
   345     /* Create the thread and go! */
   346 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   347     ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   348 #else
   349     ret = SDL_SYS_CreateThread(thread, args);
   350 #endif
   351     if (ret >= 0) {
   352         /* Wait for the thread function to use arguments */
   353         SDL_SemWait(args->wait);
   354     } else {
   355         /* Oops, failed.  Gotta free everything */
   356         if (thread->name) {
   357             SDL_free(thread->name);
   358         }
   359         SDL_free(thread);
   360         thread = NULL;
   361     }
   362     SDL_DestroySemaphore(args->wait);
   363     SDL_free(args);
   364 
   365     /* Everything is running now */
   366     return (thread);
   367 }
   368 
   369 SDL_threadID
   370 SDL_GetThreadID(SDL_Thread * thread)
   371 {
   372     SDL_threadID id;
   373 
   374     if (thread) {
   375         id = thread->threadid;
   376     } else {
   377         id = SDL_ThreadID();
   378     }
   379     return id;
   380 }
   381 
   382 const char *
   383 SDL_GetThreadName(SDL_Thread * thread)
   384 {
   385     if (thread) {
   386         return thread->name;
   387     } else {
   388         return NULL;
   389     }
   390 }
   391 
   392 int
   393 SDL_SetThreadPriority(SDL_ThreadPriority priority)
   394 {
   395     return SDL_SYS_SetThreadPriority(priority);
   396 }
   397 
   398 void
   399 SDL_WaitThread(SDL_Thread * thread, int *status)
   400 {
   401     if (thread) {
   402         SDL_SYS_WaitThread(thread);
   403         if (status) {
   404             *status = thread->status;
   405         }
   406         if (thread->name) {
   407             SDL_free(thread->name);
   408         }
   409         SDL_free(thread);
   410     }
   411 }
   412 
   413 /* vi: set ts=4 sw=4 expandtab: */