mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-03 07:01:06 +00:00 
			
		
		
		
	
		
			
	
	
		
			585 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			585 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								  Simple DirectMedia Layer
							 | 
						||
| 
								 | 
							
								  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  This software is provided 'as-is', without any express or implied
							 | 
						||
| 
								 | 
							
								  warranty.  In no event will the authors be held liable for any damages
							 | 
						||
| 
								 | 
							
								  arising from the use of this software.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Permission is granted to anyone to use this software for any purpose,
							 | 
						||
| 
								 | 
							
								  including commercial applications, and to alter it and redistribute it
							 | 
						||
| 
								 | 
							
								  freely, subject to the following restrictions:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  1. The origin of this software must not be misrepresented; you must not
							 | 
						||
| 
								 | 
							
								     claim that you wrote the original software. If you use this software
							 | 
						||
| 
								 | 
							
								     in a product, an acknowledgment in the product documentation would be
							 | 
						||
| 
								 | 
							
								     appreciated but is not required.
							 | 
						||
| 
								 | 
							
								  2. Altered source versions must be plainly marked as such, and must not be
							 | 
						||
| 
								 | 
							
								     misrepresented as being the original software.
							 | 
						||
| 
								 | 
							
								  3. This notice may not be removed or altered from any source distribution.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								#include "SDL_internal.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// System independent thread management routines for SDL
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "SDL_thread_c.h"
							 | 
						||
| 
								 | 
							
								#include "SDL_systhread.h"
							 | 
						||
| 
								 | 
							
								#include "../SDL_error_c.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// The storage is local to the thread, but the IDs are global for the process
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static SDL_AtomicInt SDL_tls_allocated;
							 | 
						||
| 
								 | 
							
								static SDL_AtomicInt SDL_tls_id;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_InitTLSData(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_SYS_InitTLSData();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void *SDL_GetTLS(SDL_TLSID *id)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_TLSData *storage;
							 | 
						||
| 
								 | 
							
								    int storage_index;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (id == NULL) {
							 | 
						||
| 
								 | 
							
								        SDL_InvalidParamError("id");
							 | 
						||
| 
								 | 
							
								        return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    storage_index = SDL_GetAtomicInt(id) - 1;
							 | 
						||
| 
								 | 
							
								    storage = SDL_SYS_GetTLSData();
							 | 
						||
| 
								 | 
							
								    if (!storage || storage_index < 0 || storage_index >= storage->limit) {
							 | 
						||
| 
								 | 
							
								        return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return storage->array[storage_index].data;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_SetTLS(SDL_TLSID *id, const void *value, SDL_TLSDestructorCallback destructor)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_TLSData *storage;
							 | 
						||
| 
								 | 
							
								    int storage_index;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (id == NULL) {
							 | 
						||
| 
								 | 
							
								        return SDL_InvalidParamError("id");
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /* Make sure TLS is initialized.
							 | 
						||
| 
								 | 
							
								     * There's a race condition here if you are calling this from non-SDL threads
							 | 
						||
| 
								 | 
							
								     * and haven't called SDL_Init() on your main thread, but such is life.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    SDL_InitTLSData();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Get the storage index associated with the ID in a thread-safe way
							 | 
						||
| 
								 | 
							
								    storage_index = SDL_GetAtomicInt(id) - 1;
							 | 
						||
| 
								 | 
							
								    if (storage_index < 0) {
							 | 
						||
| 
								 | 
							
								        int new_id = (SDL_AtomicIncRef(&SDL_tls_id) + 1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        SDL_CompareAndSwapAtomicInt(id, 0, new_id);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /* If there was a race condition we'll have wasted an ID, but every thread
							 | 
						||
| 
								 | 
							
								         * will have the same storage index for this id.
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        storage_index = SDL_GetAtomicInt(id) - 1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Get the storage for the current thread
							 | 
						||
| 
								 | 
							
								    storage = SDL_SYS_GetTLSData();
							 | 
						||
| 
								 | 
							
								    if (!storage || storage_index >= storage->limit) {
							 | 
						||
| 
								 | 
							
								        unsigned int i, oldlimit, newlimit;
							 | 
						||
| 
								 | 
							
								        SDL_TLSData *new_storage;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        oldlimit = storage ? storage->limit : 0;
							 | 
						||
| 
								 | 
							
								        newlimit = (storage_index + TLS_ALLOC_CHUNKSIZE);
							 | 
						||
| 
								 | 
							
								        new_storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0]));
							 | 
						||
| 
								 | 
							
								        if (!new_storage) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        storage = new_storage;
							 | 
						||
| 
								 | 
							
								        storage->limit = newlimit;
							 | 
						||
| 
								 | 
							
								        for (i = oldlimit; i < newlimit; ++i) {
							 | 
						||
| 
								 | 
							
								            storage->array[i].data = NULL;
							 | 
						||
| 
								 | 
							
								            storage->array[i].destructor = NULL;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!SDL_SYS_SetTLSData(storage)) {
							 | 
						||
| 
								 | 
							
								            SDL_free(storage);
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        SDL_AtomicIncRef(&SDL_tls_allocated);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    storage->array[storage_index].data = SDL_const_cast(void *, value);
							 | 
						||
| 
								 | 
							
								    storage->array[storage_index].destructor = destructor;
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_CleanupTLS(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_TLSData *storage;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Cleanup the storage for the current thread
							 | 
						||
| 
								 | 
							
								    storage = SDL_SYS_GetTLSData();
							 | 
						||
| 
								 | 
							
								    if (storage) {
							 | 
						||
| 
								 | 
							
								        int i;
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < storage->limit; ++i) {
							 | 
						||
| 
								 | 
							
								            if (storage->array[i].destructor) {
							 | 
						||
| 
								 | 
							
								                storage->array[i].destructor(storage->array[i].data);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        SDL_SYS_SetTLSData(NULL);
							 | 
						||
| 
								 | 
							
								        SDL_free(storage);
							 | 
						||
| 
								 | 
							
								        (void)SDL_AtomicDecRef(&SDL_tls_allocated);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_QuitTLSData(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_CleanupTLS();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (SDL_GetAtomicInt(&SDL_tls_allocated) == 0) {
							 | 
						||
| 
								 | 
							
								        SDL_SYS_QuitTLSData();
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        // Some thread hasn't called SDL_CleanupTLS()
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* This is a generic implementation of thread-local storage which doesn't
							 | 
						||
| 
								 | 
							
								   require additional OS support.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								   It is not especially efficient and doesn't clean up thread-local storage
							 | 
						||
| 
								 | 
							
								   as threads exit.  If there is a real OS that doesn't support thread-local
							 | 
						||
| 
								 | 
							
								   storage this implementation should be improved to be production quality.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct SDL_TLSEntry
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_ThreadID thread;
							 | 
						||
| 
								 | 
							
								    SDL_TLSData *storage;
							 | 
						||
| 
								 | 
							
								    struct SDL_TLSEntry *next;
							 | 
						||
| 
								 | 
							
								} SDL_TLSEntry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static SDL_Mutex *SDL_generic_TLS_mutex;
							 | 
						||
| 
								 | 
							
								static SDL_TLSEntry *SDL_generic_TLS;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_Generic_InitTLSData(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!SDL_generic_TLS_mutex) {
							 | 
						||
| 
								 | 
							
								        SDL_generic_TLS_mutex = SDL_CreateMutex();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SDL_TLSData *SDL_Generic_GetTLSData(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_ThreadID thread = SDL_GetCurrentThreadID();
							 | 
						||
| 
								 | 
							
								    SDL_TLSEntry *entry;
							 | 
						||
| 
								 | 
							
								    SDL_TLSData *storage = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SDL_LockMutex(SDL_generic_TLS_mutex);
							 | 
						||
| 
								 | 
							
								    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
							 | 
						||
| 
								 | 
							
								        if (entry->thread == thread) {
							 | 
						||
| 
								 | 
							
								            storage = entry->storage;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    SDL_UnlockMutex(SDL_generic_TLS_mutex);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return storage;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_Generic_SetTLSData(SDL_TLSData *data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_ThreadID thread = SDL_GetCurrentThreadID();
							 | 
						||
| 
								 | 
							
								    SDL_TLSEntry *prev, *entry;
							 | 
						||
| 
								 | 
							
								    bool result = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SDL_LockMutex(SDL_generic_TLS_mutex);
							 | 
						||
| 
								 | 
							
								    prev = NULL;
							 | 
						||
| 
								 | 
							
								    for (entry = SDL_generic_TLS; entry; entry = entry->next) {
							 | 
						||
| 
								 | 
							
								        if (entry->thread == thread) {
							 | 
						||
| 
								 | 
							
								            if (data) {
							 | 
						||
| 
								 | 
							
								                entry->storage = data;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                if (prev) {
							 | 
						||
| 
								 | 
							
								                    prev->next = entry->next;
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    SDL_generic_TLS = entry->next;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                SDL_free(entry);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        prev = entry;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (!entry && data) {
							 | 
						||
| 
								 | 
							
								        entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
							 | 
						||
| 
								 | 
							
								        if (entry) {
							 | 
						||
| 
								 | 
							
								            entry->thread = thread;
							 | 
						||
| 
								 | 
							
								            entry->storage = data;
							 | 
						||
| 
								 | 
							
								            entry->next = SDL_generic_TLS;
							 | 
						||
| 
								 | 
							
								            SDL_generic_TLS = entry;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            result = false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    SDL_UnlockMutex(SDL_generic_TLS_mutex);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return result;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_Generic_QuitTLSData(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_TLSEntry *entry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // This should have been cleaned up by the time we get here
							 | 
						||
| 
								 | 
							
								    SDL_assert(!SDL_generic_TLS);
							 | 
						||
| 
								 | 
							
								    if (SDL_generic_TLS) {
							 | 
						||
| 
								 | 
							
								        SDL_LockMutex(SDL_generic_TLS_mutex);
							 | 
						||
| 
								 | 
							
								        for (entry = SDL_generic_TLS; entry; ) {
							 | 
						||
| 
								 | 
							
								            SDL_TLSEntry *next = entry->next;
							 | 
						||
| 
								 | 
							
								            SDL_free(entry->storage);
							 | 
						||
| 
								 | 
							
								            SDL_free(entry);
							 | 
						||
| 
								 | 
							
								            entry = next;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        SDL_generic_TLS = NULL;
							 | 
						||
| 
								 | 
							
								        SDL_UnlockMutex(SDL_generic_TLS_mutex);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (SDL_generic_TLS_mutex) {
							 | 
						||
| 
								 | 
							
								        SDL_DestroyMutex(SDL_generic_TLS_mutex);
							 | 
						||
| 
								 | 
							
								        SDL_generic_TLS_mutex = NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Non-thread-safe global error variable
							 | 
						||
| 
								 | 
							
								static SDL_error *SDL_GetStaticErrBuf(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    static SDL_error SDL_global_error;
							 | 
						||
| 
								 | 
							
								    static char SDL_global_error_str[128];
							 | 
						||
| 
								 | 
							
								    SDL_global_error.str = SDL_global_error_str;
							 | 
						||
| 
								 | 
							
								    SDL_global_error.len = sizeof(SDL_global_error_str);
							 | 
						||
| 
								 | 
							
								    return &SDL_global_error;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifndef SDL_THREADS_DISABLED
							 | 
						||
| 
								 | 
							
								static void SDLCALL SDL_FreeErrBuf(void *data)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_error *errbuf = (SDL_error *)data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (errbuf->str) {
							 | 
						||
| 
								 | 
							
								        errbuf->free_func(errbuf->str);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    errbuf->free_func(errbuf);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Routine to get the thread-specific error variable
							 | 
						||
| 
								 | 
							
								SDL_error *SDL_GetErrBuf(bool create)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								#ifdef SDL_THREADS_DISABLED
							 | 
						||
| 
								 | 
							
								    return SDL_GetStaticErrBuf();
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								    static SDL_TLSID tls_errbuf;
							 | 
						||
| 
								 | 
							
								    SDL_error *errbuf;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    errbuf = (SDL_error *)SDL_GetTLS(&tls_errbuf);
							 | 
						||
| 
								 | 
							
								    if (!errbuf) {
							 | 
						||
| 
								 | 
							
								        if (!create) {
							 | 
						||
| 
								 | 
							
								            return NULL;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        /* Get the original memory functions for this allocation because the lifetime
							 | 
						||
| 
								 | 
							
								         * of the error buffer may span calls to SDL_SetMemoryFunctions() by the app
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        SDL_realloc_func realloc_func;
							 | 
						||
| 
								 | 
							
								        SDL_free_func free_func;
							 | 
						||
| 
								 | 
							
								        SDL_GetOriginalMemoryFunctions(NULL, NULL, &realloc_func, &free_func);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf));
							 | 
						||
| 
								 | 
							
								        if (!errbuf) {
							 | 
						||
| 
								 | 
							
								            return SDL_GetStaticErrBuf();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        SDL_zerop(errbuf);
							 | 
						||
| 
								 | 
							
								        errbuf->realloc_func = realloc_func;
							 | 
						||
| 
								 | 
							
								        errbuf->free_func = free_func;
							 | 
						||
| 
								 | 
							
								        SDL_SetTLS(&tls_errbuf, errbuf, SDL_FreeErrBuf);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return errbuf;
							 | 
						||
| 
								 | 
							
								#endif // SDL_THREADS_DISABLED
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool ThreadValid(SDL_Thread *thread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return SDL_ObjectValid(thread, SDL_OBJECT_TYPE_THREAD);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_RunThread(SDL_Thread *thread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    void *userdata = thread->userdata;
							 | 
						||
| 
								 | 
							
								    int(SDLCALL *userfunc)(void *) = thread->userfunc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    int *statusloc = &thread->status;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Perform any system-dependent setup - this function may not fail
							 | 
						||
| 
								 | 
							
								    SDL_SYS_SetupThread(thread->name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Get the thread id
							 | 
						||
| 
								 | 
							
								    thread->threadid = SDL_GetCurrentThreadID();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Run the function
							 | 
						||
| 
								 | 
							
								    *statusloc = userfunc(userdata);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Clean up thread-local storage
							 | 
						||
| 
								 | 
							
								    SDL_CleanupTLS();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Mark us as ready to be joined (or detached)
							 | 
						||
| 
								 | 
							
								    if (!SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_COMPLETE)) {
							 | 
						||
| 
								 | 
							
								        // Clean up if something already detached us.
							 | 
						||
| 
								 | 
							
								        if (SDL_GetThreadState(thread) == SDL_THREAD_DETACHED) {
							 | 
						||
| 
								 | 
							
								            SDL_free(thread->name); // Can't free later, we've already cleaned up TLS
							 | 
						||
| 
								 | 
							
								            SDL_free(thread);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props,
							 | 
						||
| 
								 | 
							
								                              SDL_FunctionPointer pfnBeginThread,
							 | 
						||
| 
								 | 
							
								                              SDL_FunctionPointer pfnEndThread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    // rather than check this in every backend, just make sure it's correct upfront. Only allow non-NULL if Windows, or Microsoft GDK.
							 | 
						||
| 
								 | 
							
								    #if !defined(SDL_PLATFORM_WINDOWS)
							 | 
						||
| 
								 | 
							
								    if (pfnBeginThread || pfnEndThread) {
							 | 
						||
| 
								 | 
							
								        SDL_SetError("_beginthreadex/_endthreadex not supported on this platform");
							 | 
						||
| 
								 | 
							
								        return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    #endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SDL_ThreadFunction fn = (SDL_ThreadFunction) SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, NULL);
							 | 
						||
| 
								 | 
							
								    const char *name = SDL_GetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, NULL);
							 | 
						||
| 
								 | 
							
								    const size_t stacksize = (size_t) SDL_GetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, 0);
							 | 
						||
| 
								 | 
							
								    void *userdata = SDL_GetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!fn) {
							 | 
						||
| 
								 | 
							
								        SDL_SetError("Thread entry function is NULL");
							 | 
						||
| 
								 | 
							
								        return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SDL_InitMainThread();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SDL_Thread *thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
							 | 
						||
| 
								 | 
							
								    if (!thread) {
							 | 
						||
| 
								 | 
							
								        return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    thread->status = -1;
							 | 
						||
| 
								 | 
							
								    SDL_SetAtomicInt(&thread->state, SDL_THREAD_ALIVE);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Set up the arguments for the thread
							 | 
						||
| 
								 | 
							
								    if (name) {
							 | 
						||
| 
								 | 
							
								        thread->name = SDL_strdup(name);
							 | 
						||
| 
								 | 
							
								        if (!thread->name) {
							 | 
						||
| 
								 | 
							
								            SDL_free(thread);
							 | 
						||
| 
								 | 
							
								            return NULL;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    thread->userfunc = fn;
							 | 
						||
| 
								 | 
							
								    thread->userdata = userdata;
							 | 
						||
| 
								 | 
							
								    thread->stacksize = stacksize;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, true);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Create the thread and go!
							 | 
						||
| 
								 | 
							
								    if (!SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread)) {
							 | 
						||
| 
								 | 
							
								        // Oops, failed.  Gotta free everything
							 | 
						||
| 
								 | 
							
								        SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
							 | 
						||
| 
								 | 
							
								        SDL_free(thread->name);
							 | 
						||
| 
								 | 
							
								        SDL_free(thread);
							 | 
						||
| 
								 | 
							
								        thread = NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Everything is running now
							 | 
						||
| 
								 | 
							
								    return thread;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn,
							 | 
						||
| 
								 | 
							
								                 const char *name, void *userdata,
							 | 
						||
| 
								 | 
							
								                 SDL_FunctionPointer pfnBeginThread,
							 | 
						||
| 
								 | 
							
								                 SDL_FunctionPointer pfnEndThread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const SDL_PropertiesID props = SDL_CreateProperties();
							 | 
						||
| 
								 | 
							
								    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
							 | 
						||
| 
								 | 
							
								    SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
							 | 
						||
| 
								 | 
							
								    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
							 | 
						||
| 
								 | 
							
								    SDL_Thread *thread = SDL_CreateThreadWithPropertiesRuntime(props, pfnBeginThread, pfnEndThread);
							 | 
						||
| 
								 | 
							
								    SDL_DestroyProperties(props);
							 | 
						||
| 
								 | 
							
								    return thread;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// internal helper function, not in the public API.
							 | 
						||
| 
								 | 
							
								SDL_Thread *SDL_CreateThreadWithStackSize(SDL_ThreadFunction fn, const char *name, size_t stacksize, void *userdata)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const SDL_PropertiesID props = SDL_CreateProperties();
							 | 
						||
| 
								 | 
							
								    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER, (void *) fn);
							 | 
						||
| 
								 | 
							
								    SDL_SetStringProperty(props, SDL_PROP_THREAD_CREATE_NAME_STRING, name);
							 | 
						||
| 
								 | 
							
								    SDL_SetPointerProperty(props, SDL_PROP_THREAD_CREATE_USERDATA_POINTER, userdata);
							 | 
						||
| 
								 | 
							
								    SDL_SetNumberProperty(props, SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER, (Sint64) stacksize);
							 | 
						||
| 
								 | 
							
								    SDL_Thread *thread = SDL_CreateThreadWithProperties(props);
							 | 
						||
| 
								 | 
							
								    SDL_DestroyProperties(props);
							 | 
						||
| 
								 | 
							
								    return thread;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_ThreadID id = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (thread) {
							 | 
						||
| 
								 | 
							
								        if (ThreadValid(thread)) {
							 | 
						||
| 
								 | 
							
								            id = thread->threadid;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        id = SDL_GetCurrentThreadID();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return id;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const char *SDL_GetThreadName(SDL_Thread *thread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (ThreadValid(thread)) {
							 | 
						||
| 
								 | 
							
								        return SDL_GetPersistentString(thread->name);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        return NULL;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return SDL_SYS_SetThreadPriority(priority);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_WaitThread(SDL_Thread *thread, int *status)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!ThreadValid(thread)) {
							 | 
						||
| 
								 | 
							
								        if (status) {
							 | 
						||
| 
								 | 
							
								            *status = -1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    SDL_SYS_WaitThread(thread);
							 | 
						||
| 
								 | 
							
								    if (status) {
							 | 
						||
| 
								 | 
							
								        *status = thread->status;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
							 | 
						||
| 
								 | 
							
								    SDL_free(thread->name);
							 | 
						||
| 
								 | 
							
								    SDL_free(thread);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!ThreadValid(thread)) {
							 | 
						||
| 
								 | 
							
								        return SDL_THREAD_UNKNOWN;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return (SDL_ThreadState)SDL_GetAtomicInt(&thread->state);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_DetachThread(SDL_Thread *thread)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (!ThreadValid(thread)) {
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // The thread may vanish at any time, it's no longer valid
							 | 
						||
| 
								 | 
							
								    SDL_SetObjectValid(thread, SDL_OBJECT_TYPE_THREAD, false);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Grab dibs if the state is alive+joinable.
							 | 
						||
| 
								 | 
							
								    if (SDL_CompareAndSwapAtomicInt(&thread->state, SDL_THREAD_ALIVE, SDL_THREAD_DETACHED)) {
							 | 
						||
| 
								 | 
							
								        SDL_SYS_DetachThread(thread);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        // all other states are pretty final, see where we landed.
							 | 
						||
| 
								 | 
							
								        SDL_ThreadState thread_state = SDL_GetThreadState(thread);
							 | 
						||
| 
								 | 
							
								        if (thread_state == SDL_THREAD_DETACHED) {
							 | 
						||
| 
								 | 
							
								            return; // already detached (you shouldn't call this twice!)
							 | 
						||
| 
								 | 
							
								        } else if (thread_state == SDL_THREAD_COMPLETE) {
							 | 
						||
| 
								 | 
							
								            SDL_WaitThread(thread, NULL); // already done, clean it up.
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_WaitSemaphore(SDL_Semaphore *sem)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_WaitSemaphoreTimeoutNS(sem, -1);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_TryWaitSemaphore(SDL_Semaphore *sem)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    return SDL_WaitSemaphoreTimeoutNS(sem, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    Sint64 timeoutNS;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (timeoutMS >= 0) {
							 | 
						||
| 
								 | 
							
								        timeoutNS = SDL_MS_TO_NS(timeoutMS);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        timeoutNS = -1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return SDL_WaitSemaphoreTimeoutNS(sem, timeoutNS);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_WaitConditionTimeoutNS(cond, mutex, -1);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    Sint64 timeoutNS;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (timeoutMS >= 0) {
							 | 
						||
| 
								 | 
							
								        timeoutNS = SDL_MS_TO_NS(timeoutMS);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        timeoutNS = -1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return SDL_WaitConditionTimeoutNS(cond, mutex, timeoutNS);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_ShouldInit(SDL_InitState *state)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_INITIALIZED) {
							 | 
						||
| 
								 | 
							
								        if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED, SDL_INIT_STATUS_INITIALIZING)) {
							 | 
						||
| 
								 | 
							
								            state->thread = SDL_GetCurrentThreadID();
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Wait for the other thread to complete transition
							 | 
						||
| 
								 | 
							
								        SDL_Delay(1);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool SDL_ShouldQuit(SDL_InitState *state)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    while (SDL_GetAtomicInt(&state->status) != SDL_INIT_STATUS_UNINITIALIZED) {
							 | 
						||
| 
								 | 
							
								        if (SDL_CompareAndSwapAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED, SDL_INIT_STATUS_UNINITIALIZING)) {
							 | 
						||
| 
								 | 
							
								            state->thread = SDL_GetCurrentThreadID();
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Wait for the other thread to complete transition
							 | 
						||
| 
								 | 
							
								        SDL_Delay(1);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void SDL_SetInitialized(SDL_InitState *state, bool initialized)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    SDL_assert(state->thread == SDL_GetCurrentThreadID());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (initialized) {
							 | 
						||
| 
								 | 
							
								        SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_INITIALIZED);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        SDL_SetAtomicInt(&state->status, SDL_INIT_STATUS_UNINITIALIZED);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 |