cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_thread.c (12401B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 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_internal.h"
     22
     23/* System independent thread management routines for SDL */
     24
     25#include "SDL_assert.h"
     26#include "SDL_thread.h"
     27#include "SDL_thread_c.h"
     28#include "SDL_systhread.h"
     29#include "../SDL_error_c.h"
     30
     31
     32SDL_TLSID
     33SDL_TLSCreate()
     34{
     35    static SDL_atomic_t SDL_tls_id;
     36    return SDL_AtomicIncRef(&SDL_tls_id)+1;
     37}
     38
     39void *
     40SDL_TLSGet(SDL_TLSID id)
     41{
     42    SDL_TLSData *storage;
     43
     44    storage = SDL_SYS_GetTLSData();
     45    if (!storage || id == 0 || id > storage->limit) {
     46        return NULL;
     47    }
     48    return storage->array[id-1].data;
     49}
     50
     51int
     52SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void *))
     53{
     54    SDL_TLSData *storage;
     55
     56    if (id == 0) {
     57        return SDL_InvalidParamError("id");
     58    }
     59
     60    storage = SDL_SYS_GetTLSData();
     61    if (!storage || (id > storage->limit)) {
     62        unsigned int i, oldlimit, newlimit;
     63
     64        oldlimit = storage ? storage->limit : 0;
     65        newlimit = (id + TLS_ALLOC_CHUNKSIZE);
     66        storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage)+(newlimit-1)*sizeof(storage->array[0]));
     67        if (!storage) {
     68            return SDL_OutOfMemory();
     69        }
     70        storage->limit = newlimit;
     71        for (i = oldlimit; i < newlimit; ++i) {
     72            storage->array[i].data = NULL;
     73            storage->array[i].destructor = NULL;
     74        }
     75        if (SDL_SYS_SetTLSData(storage) != 0) {
     76            return -1;
     77        }
     78    }
     79
     80    storage->array[id-1].data = SDL_const_cast(void*, value);
     81    storage->array[id-1].destructor = destructor;
     82    return 0;
     83}
     84
     85static void
     86SDL_TLSCleanup()
     87{
     88    SDL_TLSData *storage;
     89
     90    storage = SDL_SYS_GetTLSData();
     91    if (storage) {
     92        unsigned int i;
     93        for (i = 0; i < storage->limit; ++i) {
     94            if (storage->array[i].destructor) {
     95                storage->array[i].destructor(storage->array[i].data);
     96            }
     97        }
     98        SDL_SYS_SetTLSData(NULL);
     99        SDL_free(storage);
    100    }
    101}
    102
    103
    104/* This is a generic implementation of thread-local storage which doesn't
    105   require additional OS support.
    106
    107   It is not especially efficient and doesn't clean up thread-local storage
    108   as threads exit.  If there is a real OS that doesn't support thread-local
    109   storage this implementation should be improved to be production quality.
    110*/
    111
    112typedef struct SDL_TLSEntry {
    113    SDL_threadID thread;
    114    SDL_TLSData *storage;
    115    struct SDL_TLSEntry *next;
    116} SDL_TLSEntry;
    117
    118static SDL_mutex *SDL_generic_TLS_mutex;
    119static SDL_TLSEntry *SDL_generic_TLS;
    120
    121
    122SDL_TLSData *
    123SDL_Generic_GetTLSData()
    124{
    125    SDL_threadID thread = SDL_ThreadID();
    126    SDL_TLSEntry *entry;
    127    SDL_TLSData *storage = NULL;
    128
    129#if !SDL_THREADS_DISABLED
    130    if (!SDL_generic_TLS_mutex) {
    131        static SDL_SpinLock tls_lock;
    132        SDL_AtomicLock(&tls_lock);
    133        if (!SDL_generic_TLS_mutex) {
    134            SDL_mutex *mutex = SDL_CreateMutex();
    135            SDL_MemoryBarrierRelease();
    136            SDL_generic_TLS_mutex = mutex;
    137            if (!SDL_generic_TLS_mutex) {
    138                SDL_AtomicUnlock(&tls_lock);
    139                return NULL;
    140            }
    141        }
    142        SDL_AtomicUnlock(&tls_lock);
    143    }
    144#endif /* SDL_THREADS_DISABLED */
    145
    146    SDL_MemoryBarrierAcquire();
    147    SDL_LockMutex(SDL_generic_TLS_mutex);
    148    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
    149        if (entry->thread == thread) {
    150            storage = entry->storage;
    151            break;
    152        }
    153    }
    154#if !SDL_THREADS_DISABLED
    155    SDL_UnlockMutex(SDL_generic_TLS_mutex);
    156#endif
    157
    158    return storage;
    159}
    160
    161int
    162SDL_Generic_SetTLSData(SDL_TLSData *storage)
    163{
    164    SDL_threadID thread = SDL_ThreadID();
    165    SDL_TLSEntry *prev, *entry;
    166
    167    /* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
    168    SDL_LockMutex(SDL_generic_TLS_mutex);
    169    prev = NULL;
    170    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
    171        if (entry->thread == thread) {
    172            if (storage) {
    173                entry->storage = storage;
    174            } else {
    175                if (prev) {
    176                    prev->next = entry->next;
    177                } else {
    178                    SDL_generic_TLS = entry->next;
    179                }
    180                SDL_free(entry);
    181            }
    182            break;
    183        }
    184        prev = entry;
    185    }
    186    if (!entry) {
    187        entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
    188        if (entry) {
    189            entry->thread = thread;
    190            entry->storage = storage;
    191            entry->next = SDL_generic_TLS;
    192            SDL_generic_TLS = entry;
    193        }
    194    }
    195    SDL_UnlockMutex(SDL_generic_TLS_mutex);
    196
    197    if (!entry) {
    198        return SDL_OutOfMemory();
    199    }
    200    return 0;
    201}
    202
    203/* Routine to get the thread-specific error variable */
    204SDL_error *
    205SDL_GetErrBuf(void)
    206{
    207    static SDL_SpinLock tls_lock;
    208    static SDL_bool tls_being_created;
    209    static SDL_TLSID tls_errbuf;
    210    static SDL_error SDL_global_errbuf;
    211    const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
    212    SDL_error *errbuf;
    213
    214    /* tls_being_created is there simply to prevent recursion if SDL_TLSCreate() fails.
    215       It also means it's possible for another thread to also use SDL_global_errbuf,
    216       but that's very unlikely and hopefully won't cause issues.
    217     */
    218    if (!tls_errbuf && !tls_being_created) {
    219        SDL_AtomicLock(&tls_lock);
    220        if (!tls_errbuf) {
    221            SDL_TLSID slot;
    222            tls_being_created = SDL_TRUE;
    223            slot = SDL_TLSCreate();
    224            tls_being_created = SDL_FALSE;
    225            SDL_MemoryBarrierRelease();
    226            tls_errbuf = slot;
    227        }
    228        SDL_AtomicUnlock(&tls_lock);
    229    }
    230    if (!tls_errbuf) {
    231        return &SDL_global_errbuf;
    232    }
    233
    234    SDL_MemoryBarrierAcquire();
    235    errbuf = (SDL_error *)SDL_TLSGet(tls_errbuf);
    236    if (errbuf == ALLOCATION_IN_PROGRESS) {
    237        return &SDL_global_errbuf;
    238    }
    239    if (!errbuf) {
    240        /* Mark that we're in the middle of allocating our buffer */
    241        SDL_TLSSet(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
    242        errbuf = (SDL_error *)SDL_malloc(sizeof(*errbuf));
    243        if (!errbuf) {
    244            SDL_TLSSet(tls_errbuf, NULL, NULL);
    245            return &SDL_global_errbuf;
    246        }
    247        SDL_zerop(errbuf);
    248        SDL_TLSSet(tls_errbuf, errbuf, SDL_free);
    249    }
    250    return errbuf;
    251}
    252
    253
    254/* Arguments and callback to setup and run the user thread function */
    255typedef struct
    256{
    257    int (SDLCALL * func) (void *);
    258    void *data;
    259    SDL_Thread *info;
    260    SDL_sem *wait;
    261} thread_args;
    262
    263void
    264SDL_RunThread(void *data)
    265{
    266    thread_args *args = (thread_args *) data;
    267    int (SDLCALL * userfunc) (void *) = args->func;
    268    void *userdata = args->data;
    269    SDL_Thread *thread = args->info;
    270    int *statusloc = &thread->status;
    271
    272    /* Perform any system-dependent setup - this function may not fail */
    273    SDL_SYS_SetupThread(thread->name);
    274
    275    /* Get the thread id */
    276    thread->threadid = SDL_ThreadID();
    277
    278    /* Wake up the parent thread */
    279    SDL_SemPost(args->wait);
    280
    281    /* Run the function */
    282    *statusloc = userfunc(userdata);
    283
    284    /* Clean up thread-local storage */
    285    SDL_TLSCleanup();
    286
    287    /* Mark us as ready to be joined (or detached) */
    288    if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
    289        /* Clean up if something already detached us. */
    290        if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
    291            if (thread->name) {
    292                SDL_free(thread->name);
    293            }
    294            SDL_free(thread);
    295        }
    296    }
    297}
    298
    299#ifdef SDL_CreateThread
    300#undef SDL_CreateThread
    301#endif
    302#if SDL_DYNAMIC_API
    303#define SDL_CreateThread SDL_CreateThread_REAL
    304#endif
    305
    306#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
    307DECLSPEC SDL_Thread *SDLCALL
    308SDL_CreateThread(int (SDLCALL * fn) (void *),
    309                 const char *name, void *data,
    310                 pfnSDL_CurrentBeginThread pfnBeginThread,
    311                 pfnSDL_CurrentEndThread pfnEndThread)
    312#else
    313DECLSPEC SDL_Thread *SDLCALL
    314SDL_CreateThread(int (SDLCALL * fn) (void *),
    315                 const char *name, void *data)
    316#endif
    317{
    318    SDL_Thread *thread;
    319    thread_args *args;
    320    int ret;
    321
    322    /* Allocate memory for the thread info structure */
    323    thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
    324    if (thread == NULL) {
    325        SDL_OutOfMemory();
    326        return (NULL);
    327    }
    328    SDL_zerop(thread);
    329    thread->status = -1;
    330    SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
    331
    332    /* Set up the arguments for the thread */
    333    if (name != NULL) {
    334        thread->name = SDL_strdup(name);
    335        if (thread->name == NULL) {
    336            SDL_OutOfMemory();
    337            SDL_free(thread);
    338            return (NULL);
    339        }
    340    }
    341
    342    /* Set up the arguments for the thread */
    343    args = (thread_args *) SDL_malloc(sizeof(*args));
    344    if (args == NULL) {
    345        SDL_OutOfMemory();
    346        if (thread->name) {
    347            SDL_free(thread->name);
    348        }
    349        SDL_free(thread);
    350        return (NULL);
    351    }
    352    args->func = fn;
    353    args->data = data;
    354    args->info = thread;
    355    args->wait = SDL_CreateSemaphore(0);
    356    if (args->wait == NULL) {
    357        if (thread->name) {
    358            SDL_free(thread->name);
    359        }
    360        SDL_free(thread);
    361        SDL_free(args);
    362        return (NULL);
    363    }
    364
    365    /* Create the thread and go! */
    366#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
    367    ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
    368#else
    369    ret = SDL_SYS_CreateThread(thread, args);
    370#endif
    371    if (ret >= 0) {
    372        /* Wait for the thread function to use arguments */
    373        SDL_SemWait(args->wait);
    374    } else {
    375        /* Oops, failed.  Gotta free everything */
    376        if (thread->name) {
    377            SDL_free(thread->name);
    378        }
    379        SDL_free(thread);
    380        thread = NULL;
    381    }
    382    SDL_DestroySemaphore(args->wait);
    383    SDL_free(args);
    384
    385    /* Everything is running now */
    386    return (thread);
    387}
    388
    389SDL_threadID
    390SDL_GetThreadID(SDL_Thread * thread)
    391{
    392    SDL_threadID id;
    393
    394    if (thread) {
    395        id = thread->threadid;
    396    } else {
    397        id = SDL_ThreadID();
    398    }
    399    return id;
    400}
    401
    402const char *
    403SDL_GetThreadName(SDL_Thread * thread)
    404{
    405    if (thread) {
    406        return thread->name;
    407    } else {
    408        return NULL;
    409    }
    410}
    411
    412int
    413SDL_SetThreadPriority(SDL_ThreadPriority priority)
    414{
    415    return SDL_SYS_SetThreadPriority(priority);
    416}
    417
    418void
    419SDL_WaitThread(SDL_Thread * thread, int *status)
    420{
    421    if (thread) {
    422        SDL_SYS_WaitThread(thread);
    423        if (status) {
    424            *status = thread->status;
    425        }
    426        if (thread->name) {
    427            SDL_free(thread->name);
    428        }
    429        SDL_free(thread);
    430    }
    431}
    432
    433void
    434SDL_DetachThread(SDL_Thread * thread)
    435{
    436    if (!thread) {
    437        return;
    438    }
    439
    440    /* Grab dibs if the state is alive+joinable. */
    441    if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
    442        SDL_SYS_DetachThread(thread);
    443    } else {
    444        /* all other states are pretty final, see where we landed. */
    445        const int thread_state = SDL_AtomicGet(&thread->state);
    446        if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
    447            return;  /* already detached (you shouldn't call this twice!) */
    448        } else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
    449            SDL_WaitThread(thread, NULL);  /* already done, clean it up. */
    450        } else {
    451            SDL_assert(0 && "Unexpected thread state");
    452        }
    453    }
    454}
    455
    456/* vi: set ts=4 sw=4 expandtab: */