Merge commit 'dec0d4ec4153bf9fc2b78ae6c2df45b6ea8dde7a' as 'external/sdl/SDL'

This commit is contained in:
2023-07-25 22:27:55 +02:00
1663 changed files with 627495 additions and 0 deletions

View File

@ -0,0 +1,77 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* These are functions that need to be implemented by a port of SDL */
#ifndef SDL_systhread_h_
#define SDL_systhread_h_
#include "SDL_thread_c.h"
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* This function creates a thread, passing args to SDL_RunThread(),
saves a system-dependent thread id in thread->id, and returns 0
on success.
*/
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
extern int SDL_SYS_CreateThread(SDL_Thread *thread,
pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread);
#else
extern int SDL_SYS_CreateThread(SDL_Thread *thread);
#endif
/* This function does any necessary setup in the child thread */
extern void SDL_SYS_SetupThread(const char *name);
/* This function sets the current thread priority */
extern int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority);
/* This function waits for the thread to finish and frees any data
allocated by SDL_SYS_CreateThread()
*/
extern void SDL_SYS_WaitThread(SDL_Thread *thread);
/* Mark thread as cleaned up as soon as it exits, without joining. */
extern void SDL_SYS_DetachThread(SDL_Thread *thread);
/* Get the thread local storage for this thread */
extern SDL_TLSData *SDL_SYS_GetTLSData(void);
/* Set the thread local storage for this thread */
extern int SDL_SYS_SetTLSData(SDL_TLSData *data);
/* This is for internal SDL use, so we don't need #ifdefs everywhere. */
extern SDL_Thread *
SDL_CreateThreadInternal(int(SDLCALL *fn)(void *), const char *name,
const size_t stacksize, void *data);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#endif /* SDL_systhread_h_ */

510
external/sdl/SDL/src/thread/SDL_thread.c vendored Normal file
View File

@ -0,0 +1,510 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
SDL_TLSID SDL_CreateTLS(void)
{
static SDL_AtomicInt SDL_tls_id;
return SDL_AtomicIncRef(&SDL_tls_id) + 1;
}
void *SDL_GetTLS(SDL_TLSID id)
{
SDL_TLSData *storage;
storage = SDL_SYS_GetTLSData();
if (storage == NULL || id == 0 || id > storage->limit) {
return NULL;
}
return storage->array[id - 1].data;
}
int SDL_SetTLS(SDL_TLSID id, const void *value, void(SDLCALL *destructor)(void *))
{
SDL_TLSData *storage;
if (id == 0) {
return SDL_InvalidParamError("id");
}
storage = SDL_SYS_GetTLSData();
if (storage == NULL || (id > storage->limit)) {
unsigned int i, oldlimit, newlimit;
oldlimit = storage ? storage->limit : 0;
newlimit = (id + TLS_ALLOC_CHUNKSIZE);
storage = (SDL_TLSData *)SDL_realloc(storage, sizeof(*storage) + (newlimit - 1) * sizeof(storage->array[0]));
if (storage == NULL) {
return SDL_OutOfMemory();
}
storage->limit = newlimit;
for (i = oldlimit; i < newlimit; ++i) {
storage->array[i].data = NULL;
storage->array[i].destructor = NULL;
}
if (SDL_SYS_SetTLSData(storage) != 0) {
return -1;
}
}
storage->array[id - 1].data = SDL_const_cast(void *, value);
storage->array[id - 1].destructor = destructor;
return 0;
}
void SDL_CleanupTLS(void)
{
SDL_TLSData *storage;
storage = SDL_SYS_GetTLSData();
if (storage) {
unsigned 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);
}
}
/* 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;
SDL_TLSData *SDL_Generic_GetTLSData(void)
{
SDL_threadID thread = SDL_ThreadID();
SDL_TLSEntry *entry;
SDL_TLSData *storage = NULL;
#ifndef SDL_THREADS_DISABLED
if (SDL_generic_TLS_mutex == NULL) {
static SDL_SpinLock tls_lock;
SDL_AtomicLock(&tls_lock);
if (SDL_generic_TLS_mutex == NULL) {
SDL_Mutex *mutex = SDL_CreateMutex();
SDL_MemoryBarrierRelease();
SDL_generic_TLS_mutex = mutex;
if (SDL_generic_TLS_mutex == NULL) {
SDL_AtomicUnlock(&tls_lock);
return NULL;
}
}
SDL_AtomicUnlock(&tls_lock);
}
SDL_MemoryBarrierAcquire();
SDL_LockMutex(SDL_generic_TLS_mutex);
#endif /* SDL_THREADS_DISABLED */
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
storage = entry->storage;
break;
}
}
#ifndef SDL_THREADS_DISABLED
SDL_UnlockMutex(SDL_generic_TLS_mutex);
#endif
return storage;
}
int SDL_Generic_SetTLSData(SDL_TLSData *data)
{
SDL_threadID thread = SDL_ThreadID();
SDL_TLSEntry *prev, *entry;
/* SDL_Generic_GetTLSData() is always called first, so we can assume SDL_generic_TLS_mutex */
SDL_LockMutex(SDL_generic_TLS_mutex);
prev = NULL;
for (entry = SDL_generic_TLS; entry; entry = entry->next) {
if (entry->thread == thread) {
if (data != NULL) {
entry->storage = data;
} else {
if (prev != NULL) {
prev->next = entry->next;
} else {
SDL_generic_TLS = entry->next;
}
SDL_free(entry);
}
break;
}
prev = entry;
}
if (entry == NULL) {
entry = (SDL_TLSEntry *)SDL_malloc(sizeof(*entry));
if (entry) {
entry->thread = thread;
entry->storage = data;
entry->next = SDL_generic_TLS;
SDL_generic_TLS = entry;
}
}
SDL_UnlockMutex(SDL_generic_TLS_mutex);
if (entry == NULL) {
return SDL_OutOfMemory();
}
return 0;
}
/* 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(void)
{
#ifdef SDL_THREADS_DISABLED
return SDL_GetStaticErrBuf();
#else
static SDL_SpinLock tls_lock;
static SDL_bool tls_being_created;
static SDL_TLSID tls_errbuf;
const SDL_error *ALLOCATION_IN_PROGRESS = (SDL_error *)-1;
SDL_error *errbuf;
/* tls_being_created is there simply to prevent recursion if SDL_CreateTLS() fails.
It also means it's possible for another thread to also use SDL_global_errbuf,
but that's very unlikely and hopefully won't cause issues.
*/
if (!tls_errbuf && !tls_being_created) {
SDL_AtomicLock(&tls_lock);
if (!tls_errbuf) {
SDL_TLSID slot;
tls_being_created = SDL_TRUE;
slot = SDL_CreateTLS();
tls_being_created = SDL_FALSE;
SDL_MemoryBarrierRelease();
tls_errbuf = slot;
}
SDL_AtomicUnlock(&tls_lock);
}
if (!tls_errbuf) {
return SDL_GetStaticErrBuf();
}
SDL_MemoryBarrierAcquire();
errbuf = (SDL_error *)SDL_GetTLS(tls_errbuf);
if (errbuf == ALLOCATION_IN_PROGRESS) {
return SDL_GetStaticErrBuf();
}
if (errbuf == 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);
/* Mark that we're in the middle of allocating our buffer */
SDL_SetTLS(tls_errbuf, ALLOCATION_IN_PROGRESS, NULL);
errbuf = (SDL_error *)realloc_func(NULL, sizeof(*errbuf));
if (errbuf == NULL) {
SDL_SetTLS(tls_errbuf, NULL, NULL);
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 */
}
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_ThreadID();
/* Run the function */
*statusloc = userfunc(userdata);
/* Clean up thread-local storage */
SDL_CleanupTLS();
/* Mark us as ready to be joined (or detached) */
if (!SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_ZOMBIE)) {
/* Clean up if something already detached us. */
if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_DETACHED, SDL_THREAD_STATE_CLEANED)) {
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
}
}
}
#ifdef SDL_CreateThread
#undef SDL_CreateThread
#undef SDL_CreateThreadWithStackSize
#endif
#if SDL_DYNAMIC_API
#define SDL_CreateThread SDL_CreateThread_REAL
#define SDL_CreateThreadWithStackSize SDL_CreateThreadWithStackSize_REAL
#endif
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
SDL_Thread *SDL_CreateThreadWithStackSize(int(SDLCALL *fn)(void *),
const char *name, const size_t stacksize, void *data,
pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread)
#else
SDL_Thread *SDL_CreateThreadWithStackSize(int(SDLCALL *fn)(void *),
const char *name, const size_t stacksize, void *data)
#endif
{
SDL_Thread *thread;
int ret;
/* Allocate memory for the thread info structure */
thread = (SDL_Thread *)SDL_calloc(1, sizeof(*thread));
if (thread == NULL) {
SDL_OutOfMemory();
return NULL;
}
thread->status = -1;
SDL_AtomicSet(&thread->state, SDL_THREAD_STATE_ALIVE);
/* Set up the arguments for the thread */
if (name != NULL) {
thread->name = SDL_strdup(name);
if (thread->name == NULL) {
SDL_OutOfMemory();
SDL_free(thread);
return NULL;
}
}
thread->userfunc = fn;
thread->userdata = data;
thread->stacksize = stacksize;
/* Create the thread and go! */
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
ret = SDL_SYS_CreateThread(thread, pfnBeginThread, pfnEndThread);
#else
ret = SDL_SYS_CreateThread(thread);
#endif
if (ret < 0) {
/* Oops, failed. Gotta free everything */
SDL_free(thread->name);
SDL_free(thread);
thread = NULL;
}
/* Everything is running now */
return thread;
}
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
DECLSPEC SDL_Thread *SDLCALL SDL_CreateThread(int(SDLCALL *fn)(void *),
const char *name, void *data,
pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread)
#else
DECLSPEC SDL_Thread *SDLCALL SDL_CreateThread(int(SDLCALL *fn)(void *),
const char *name, void *data)
#endif
{
/* !!! FIXME: in 2.1, just make stackhint part of the usual API. */
const char *stackhint = SDL_GetHint(SDL_HINT_THREAD_STACK_SIZE);
size_t stacksize = 0;
/* If the SDL_HINT_THREAD_STACK_SIZE exists, use it */
if (stackhint != NULL) {
char *endp = NULL;
const Sint64 hintval = SDL_strtoll(stackhint, &endp, 10);
if ((*stackhint != '\0') && (*endp == '\0')) { /* a valid number? */
if (hintval > 0) { /* reject bogus values. */
stacksize = (size_t)hintval;
}
}
}
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, pfnBeginThread, pfnEndThread);
#else
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
#endif
}
SDL_Thread *SDL_CreateThreadInternal(int(SDLCALL *fn)(void *), const char *name,
const size_t stacksize, void *data)
{
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data, NULL, NULL);
#else
return SDL_CreateThreadWithStackSize(fn, name, stacksize, data);
#endif
}
SDL_threadID SDL_GetThreadID(SDL_Thread *thread)
{
SDL_threadID id;
if (thread) {
id = thread->threadid;
} else {
id = SDL_ThreadID();
}
return id;
}
const char *SDL_GetThreadName(SDL_Thread *thread)
{
if (thread) {
return thread->name;
} else {
return NULL;
}
}
int SDL_SetThreadPriority(SDL_ThreadPriority priority)
{
return SDL_SYS_SetThreadPriority(priority);
}
void SDL_WaitThread(SDL_Thread *thread, int *status)
{
if (thread) {
SDL_SYS_WaitThread(thread);
if (status) {
*status = thread->status;
}
if (thread->name) {
SDL_free(thread->name);
}
SDL_free(thread);
}
}
void SDL_DetachThread(SDL_Thread *thread)
{
if (thread == NULL) {
return;
}
/* Grab dibs if the state is alive+joinable. */
if (SDL_AtomicCAS(&thread->state, SDL_THREAD_STATE_ALIVE, SDL_THREAD_STATE_DETACHED)) {
SDL_SYS_DetachThread(thread);
} else {
/* all other states are pretty final, see where we landed. */
const int thread_state = SDL_AtomicGet(&thread->state);
if ((thread_state == SDL_THREAD_STATE_DETACHED) || (thread_state == SDL_THREAD_STATE_CLEANED)) {
return; /* already detached (you shouldn't call this twice!) */
} else if (thread_state == SDL_THREAD_STATE_ZOMBIE) {
SDL_WaitThread(thread, NULL); /* already done, clean it up. */
} else {
SDL_assert(0 && "Unexpected thread state");
}
}
}
int SDL_WaitSemaphore(SDL_Semaphore *sem)
{
return SDL_WaitSemaphoreTimeoutNS(sem, SDL_MUTEX_MAXWAIT);
}
int SDL_TryWaitSemaphore(SDL_Semaphore *sem)
{
return SDL_WaitSemaphoreTimeoutNS(sem, 0);
}
int 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);
}
int SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)
{
return SDL_WaitConditionTimeoutNS(cond, mutex, SDL_MUTEX_MAXWAIT);
}
int 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);
}

View File

@ -0,0 +1,104 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_thread_c_h_
#define SDL_thread_c_h_
/* Need the definitions of SYS_ThreadHandle */
#ifdef SDL_THREADS_DISABLED
#include "generic/SDL_systhread_c.h"
#elif defined(SDL_THREAD_PTHREAD)
#include "pthread/SDL_systhread_c.h"
#elif defined(SDL_THREAD_WINDOWS)
#include "windows/SDL_systhread_c.h"
#elif defined(SDL_THREAD_PS2)
#include "ps2/SDL_systhread_c.h"
#elif defined(SDL_THREAD_PSP)
#include "psp/SDL_systhread_c.h"
#elif defined(SDL_THREAD_VITA)
#include "vita/SDL_systhread_c.h"
#elif defined(SDL_THREAD_N3DS)
#include "n3ds/SDL_systhread_c.h"
#elif defined(SDL_THREAD_STDCPP)
#include "stdcpp/SDL_systhread_c.h"
#elif defined(SDL_THREAD_NGAGE)
#include "ngage/SDL_systhread_c.h"
#else
#error Need thread implementation for this platform
#include "generic/SDL_systhread_c.h"
#endif
#include "../SDL_error_c.h"
typedef enum SDL_ThreadState
{
SDL_THREAD_STATE_ALIVE,
SDL_THREAD_STATE_DETACHED,
SDL_THREAD_STATE_ZOMBIE,
SDL_THREAD_STATE_CLEANED,
} SDL_ThreadState;
/* This is the system-independent thread info structure */
struct SDL_Thread
{
SDL_threadID threadid;
SYS_ThreadHandle handle;
int status;
SDL_AtomicInt state; /* SDL_THREAD_STATE_* */
SDL_error errbuf;
char *name;
size_t stacksize; /* 0 for default, >0 for user-specified stack size. */
int(SDLCALL *userfunc)(void *);
void *userdata;
void *data;
void *endfunc; /* only used on some platforms. */
};
/* This is the function called to run a thread */
extern void SDL_RunThread(SDL_Thread *thread);
/* This is the system-independent thread local storage structure */
typedef struct
{
unsigned int limit;
struct
{
void *data;
void(SDLCALL *destructor)(void *);
} array[1];
} SDL_TLSData;
/* This is how many TLS entries we allocate at once */
#define TLS_ALLOC_CHUNKSIZE 4
/* Get cross-platform, slow, thread local storage for this thread.
This is only intended as a fallback if getting real thread-local
storage fails or isn't supported on this platform.
*/
extern SDL_TLSData *SDL_Generic_GetTLSData(void);
/* Set cross-platform, slow, thread local storage for this thread.
This is only intended as a fallback if getting real thread-local
storage fails or isn't supported on this platform.
*/
extern int SDL_Generic_SetTLSData(SDL_TLSData *data);
#endif /* SDL_thread_c_h_ */

View File

@ -0,0 +1,218 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* An implementation of condition variables using semaphores and mutexes */
/*
This implementation borrows heavily from the BeOS condition variable
implementation, written by Christopher Tate and Owen Smith. Thanks!
*/
#include "../generic/SDL_syscond_c.h"
/* If two implementations are to be compiled into SDL (the active one
* will be chosen at runtime), the function names need to be
* suffixed
*/
#ifndef SDL_THREAD_GENERIC_COND_SUFFIX
#define SDL_CreateCondition_generic SDL_CreateCondition
#define SDL_DestroyCondition_generic SDL_DestroyCondition
#define SDL_SignalCondition_generic SDL_SignalCondition
#define SDL_BroadcastCondition_generic SDL_BroadcastCondition
#define SDL_WaitConditionTimeoutNS_generic SDL_WaitConditionTimeoutNS
#endif
typedef struct SDL_cond_generic
{
SDL_Mutex *lock;
int waiting;
int signals;
SDL_Semaphore *wait_sem;
SDL_Semaphore *wait_done;
} SDL_cond_generic;
/* Create a condition variable */
SDL_Condition *SDL_CreateCondition_generic(void)
{
SDL_cond_generic *cond;
cond = (SDL_cond_generic *)SDL_malloc(sizeof(SDL_cond_generic));
if (cond) {
cond->lock = SDL_CreateMutex();
cond->wait_sem = SDL_CreateSemaphore(0);
cond->wait_done = SDL_CreateSemaphore(0);
cond->waiting = cond->signals = 0;
if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
SDL_DestroyCondition_generic((SDL_Condition *)cond);
cond = NULL;
}
} else {
SDL_OutOfMemory();
}
return (SDL_Condition *)cond;
}
/* Destroy a condition variable */
void SDL_DestroyCondition_generic(SDL_Condition *_cond)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
if (cond) {
if (cond->wait_sem) {
SDL_DestroySemaphore(cond->wait_sem);
}
if (cond->wait_done) {
SDL_DestroySemaphore(cond->wait_done);
}
if (cond->lock) {
SDL_DestroyMutex(cond->lock);
}
SDL_free(cond);
}
}
/* Restart one of the threads that are waiting on the condition variable */
int SDL_SignalCondition_generic(SDL_Condition *_cond)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
++cond->signals;
SDL_PostSemaphore(cond->wait_sem);
SDL_UnlockMutex(cond->lock);
SDL_WaitSemaphore(cond->wait_done);
} else {
SDL_UnlockMutex(cond->lock);
}
return 0;
}
/* Restart all threads that are waiting on the condition variable */
int SDL_BroadcastCondition_generic(SDL_Condition *_cond)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
int i, num_waiting;
num_waiting = (cond->waiting - cond->signals);
cond->signals = cond->waiting;
for (i = 0; i < num_waiting; ++i) {
SDL_PostSemaphore(cond->wait_sem);
}
/* Now all released threads are blocked here, waiting for us.
Collect them all (and win fabulous prizes!) :-)
*/
SDL_UnlockMutex(cond->lock);
for (i = 0; i < num_waiting; ++i) {
SDL_WaitSemaphore(cond->wait_done);
}
} else {
SDL_UnlockMutex(cond->lock);
}
return 0;
}
/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.
The mutex must be locked before entering this function!
The mutex is unlocked during the wait, and locked again after the wait.
Typical use:
Thread A:
SDL_LockMutex(lock);
while ( ! condition ) {
SDL_WaitCondition(cond, lock);
}
SDL_UnlockMutex(lock);
Thread B:
SDL_LockMutex(lock);
...
condition = true;
...
SDL_SignalCondition(cond);
SDL_UnlockMutex(lock);
*/
int SDL_WaitConditionTimeoutNS_generic(SDL_Condition *_cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
SDL_cond_generic *cond = (SDL_cond_generic *)_cond;
int retval;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* Obtain the protection mutex, and increment the number of waiters.
This allows the signal mechanism to only perform a signal if there
are waiting threads.
*/
SDL_LockMutex(cond->lock);
++cond->waiting;
SDL_UnlockMutex(cond->lock);
/* Unlock the mutex, as is required by condition variable semantics */
SDL_UnlockMutex(mutex);
/* Wait for a signal */
retval = SDL_WaitSemaphoreTimeoutNS(cond->wait_sem, timeoutNS);
/* Let the signaler know we have completed the wait, otherwise
the signaler can race ahead and get the condition semaphore
if we are stopped between the mutex unlock and semaphore wait,
giving a deadlock. See the following URL for details:
http://web.archive.org/web/20010914175514/http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html#Workshop
*/
SDL_LockMutex(cond->lock);
if (cond->signals > 0) {
/* If we timed out, we need to eat a condition signal */
if (retval > 0) {
SDL_WaitSemaphore(cond->wait_sem);
}
/* We always notify the signal thread that we are done */
SDL_PostSemaphore(cond->wait_done);
/* Signal handshake complete */
--cond->signals;
}
--cond->waiting;
SDL_UnlockMutex(cond->lock);
/* Lock the mutex, as is required by condition variable semantics */
SDL_LockMutex(mutex);
return retval;
}

View File

@ -0,0 +1,36 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_syscond_generic_h_
#define SDL_syscond_generic_h_
#ifdef SDL_THREAD_GENERIC_COND_SUFFIX
SDL_Condition *SDL_CreateCondition_generic(void);
void SDL_DestroyCondition_generic(SDL_Condition *cond);
int SDL_SignalCondition_generic(SDL_Condition *cond);
int SDL_BroadcastCondition_generic(SDL_Condition *cond);
int SDL_WaitConditionTimeoutNS_generic(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS);
#endif /* SDL_THREAD_GENERIC_COND_SUFFIX */
#endif /* SDL_syscond_generic_h_ */

View File

@ -0,0 +1,161 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* An implementation of mutexes using semaphores */
#include "SDL_systhread_c.h"
struct SDL_Mutex
{
int recursive;
SDL_threadID owner;
SDL_Semaphore *sem;
};
/* Create a mutex */
SDL_Mutex *SDL_CreateMutex(void)
{
SDL_Mutex *mutex;
/* Allocate mutex memory */
mutex = (SDL_Mutex *)SDL_calloc(1, sizeof(*mutex));
#ifndef SDL_THREADS_DISABLED
if (mutex) {
/* Create the mutex semaphore, with initial value 1 */
mutex->sem = SDL_CreateSemaphore(1);
mutex->recursive = 0;
mutex->owner = 0;
if (!mutex->sem) {
SDL_free(mutex);
mutex = NULL;
}
} else {
SDL_OutOfMemory();
}
#endif /* !SDL_THREADS_DISABLED */
return mutex;
}
/* Free the mutex */
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
if (mutex->sem) {
SDL_DestroySemaphore(mutex->sem);
}
SDL_free(mutex);
}
}
/* Lock the mutex */
int SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
SDL_threadID this_thread;
if (mutex == NULL) {
return 0;
}
this_thread = SDL_ThreadID();
if (mutex->owner == this_thread) {
++mutex->recursive;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
SDL_WaitSemaphore(mutex->sem);
mutex->owner = this_thread;
mutex->recursive = 0;
}
return 0;
#endif /* SDL_THREADS_DISABLED */
}
/* try Lock the mutex */
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
int retval = 0;
SDL_threadID this_thread;
if (mutex == NULL) {
return 0;
}
this_thread = SDL_ThreadID();
if (mutex->owner == this_thread) {
++mutex->recursive;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
retval = SDL_WaitSemaphore(mutex->sem);
if (retval == 0) {
mutex->owner = this_thread;
mutex->recursive = 0;
}
}
return retval;
#endif /* SDL_THREADS_DISABLED */
}
/* Unlock the mutex */
int SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
if (mutex == NULL) {
return 0;
}
/* If we don't own the mutex, we can't unlock it */
if (SDL_ThreadID() != mutex->owner) {
return SDL_SetError("mutex not owned by this thread");
}
if (mutex->recursive) {
--mutex->recursive;
} else {
/* The order of operations is important.
First reset the owner so another thread doesn't lock
the mutex and set the ownership before we reset it,
then release the lock semaphore.
*/
mutex->owner = 0;
SDL_PostSemaphore(mutex->sem);
}
return 0;
#endif /* SDL_THREADS_DISABLED */
}

View File

@ -0,0 +1,21 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"

View File

@ -0,0 +1,185 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* An implementation of rwlocks using mutexes, condition variables, and atomics. */
#include "SDL_systhread_c.h"
#include "../generic/SDL_sysrwlock_c.h"
/* If two implementations are to be compiled into SDL (the active one
* will be chosen at runtime), the function names need to be
* suffixed
*/
/* !!! FIXME: this is quite a tapdance with macros and the build system, maybe we can simplify how we do this. --ryan. */
#ifndef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
#define SDL_CreateRWLock_generic SDL_CreateRWLock
#define SDL_DestroyRWLock_generic SDL_DestroyRWLock
#define SDL_LockRWLockForReading_generic SDL_LockRWLockForReading
#define SDL_LockRWLockForWriting_generic SDL_LockRWLockForWriting
#define SDL_TryLockRWLockForReading_generic SDL_TryLockRWLockForReading
#define SDL_TryLockRWLockForWriting_generic SDL_TryLockRWLockForWriting
#define SDL_UnlockRWLock_generic SDL_UnlockRWLock
#endif
struct SDL_RWLock
{
SDL_Mutex *lock;
SDL_Condition *condition;
SDL_threadID writer_thread;
SDL_AtomicInt reader_count;
SDL_AtomicInt writer_count;
};
SDL_RWLock *SDL_CreateRWLock_generic(void)
{
SDL_RWLock *rwlock = (SDL_RWLock *) SDL_malloc(sizeof (*rwlock));
if (!rwlock) {
SDL_OutOfMemory();
return NULL;
}
rwlock->lock = SDL_CreateMutex();
if (!rwlock->lock) {
SDL_free(rwlock);
return NULL;
}
rwlock->condition = SDL_CreateCondition();
if (!rwlock->condition) {
SDL_DestroyMutex(rwlock->lock);
SDL_free(rwlock);
return NULL;
}
SDL_AtomicSet(&rwlock->reader_count, 0);
SDL_AtomicSet(&rwlock->writer_count, 0);
return rwlock;
}
void SDL_DestroyRWLock_generic(SDL_RWLock *rwlock)
{
if (rwlock) {
SDL_DestroyMutex(rwlock->lock);
SDL_DestroyCondition(rwlock->condition);
SDL_free(rwlock);
}
}
int SDL_LockRWLockForReading_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
} else if (SDL_LockMutex(rwlock->lock) == -1) {
return -1;
}
SDL_assert(SDL_AtomicGet(&rwlock->writer_count) == 0); /* shouldn't be able to grab lock if there's a writer! */
SDL_AtomicAdd(&rwlock->reader_count, 1);
SDL_UnlockMutex(rwlock->lock); /* other readers can attempt to share the lock. */
return 0;
}
int SDL_LockRWLockForWriting_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
} else if (SDL_LockMutex(rwlock->lock) == -1) {
return -1;
}
while (SDL_AtomicGet(&rwlock->reader_count) > 0) { /* while something is holding the shared lock, keep waiting. */
SDL_WaitCondition(rwlock->condition, rwlock->lock); /* release the lock and wait for readers holding the shared lock to release it, regrab the lock. */
}
/* we hold the lock! */
SDL_AtomicAdd(&rwlock->writer_count, 1); /* we let these be recursive, but the API doesn't require this. It _does_ trust you unlock correctly! */
return 0;
}
int SDL_TryLockRWLockForReading_generic(SDL_RWLock *rwlock)
{
int rc;
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
}
rc = SDL_TryLockMutex(rwlock->lock);
if (rc != 0) {
/* !!! FIXME: there is a small window where a reader has to lock the mutex, and if we hit that, we will return SDL_RWLOCK_TIMEDOUT even though we could have shared the lock. */
return rc;
}
SDL_assert(SDL_AtomicGet(&rwlock->writer_count) == 0); /* shouldn't be able to grab lock if there's a writer! */
SDL_AtomicAdd(&rwlock->reader_count, 1);
SDL_UnlockMutex(rwlock->lock); /* other readers can attempt to share the lock. */
return 0;
}
int SDL_TryLockRWLockForWriting_generic(SDL_RWLock *rwlock)
{
int rc;
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
} else if ((rc = SDL_TryLockMutex(rwlock->lock)) != 0) {
return rc;
}
if (SDL_AtomicGet(&rwlock->reader_count) > 0) { /* a reader is using the shared lock, treat it as unavailable. */
SDL_UnlockMutex(rwlock->lock);
return SDL_RWLOCK_TIMEDOUT;
}
/* we hold the lock! */
SDL_AtomicAdd(&rwlock->writer_count, 1); /* we let these be recursive, but the API doesn't require this. It _does_ trust you unlock correctly! */
return 0;
}
int SDL_UnlockRWLock_generic(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
}
SDL_LockMutex(rwlock->lock); /* recursive lock for writers, readers grab lock to make sure things are sane. */
if (SDL_AtomicGet(&rwlock->reader_count) > 0) { /* we're a reader */
SDL_AtomicAdd(&rwlock->reader_count, -1);
SDL_BroadcastCondition(rwlock->condition); /* alert any pending writers to attempt to try to grab the lock again. */
} else if (SDL_AtomicGet(&rwlock->writer_count) > 0) { /* we're a writer */
SDL_AtomicAdd(&rwlock->writer_count, -1);
SDL_UnlockMutex(rwlock->lock); /* recursive unlock. */
}
SDL_UnlockMutex(rwlock->lock);
return 0;
}

View File

@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_sysrwlock_c_h_
#define SDL_sysrwlock_c_h_
#ifdef SDL_THREAD_GENERIC_RWLOCK_SUFFIX
SDL_RWLock *SDL_CreateRWLock_generic(void);
void SDL_DestroyRWLock_generic(SDL_RWLock *rwlock);
int SDL_LockRWLockForReading_generic(SDL_RWLock *rwlock);
int SDL_LockRWLockForWriting_generic(SDL_RWLock *rwlock);
int SDL_TryLockRWLockForReading_generic(SDL_RWLock *rwlock);
int SDL_TryLockRWLockForWriting_generic(SDL_RWLock *rwlock);
int SDL_UnlockRWLock_generic(SDL_RWLock *rwlock);
#endif /* SDL_THREAD_GENERIC_RWLOCK_SUFFIX */
#endif /* SDL_sysrwlock_c_h_ */

View File

@ -0,0 +1,173 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* An implementation of semaphores using mutexes and condition variables */
#include "SDL_systhread_c.h"
#ifdef SDL_THREADS_DISABLED
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_SetError("SDL not built with thread support");
return (SDL_Semaphore *)0;
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
}
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
return SDL_SetError("SDL not built with thread support");
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
return 0;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
return SDL_SetError("SDL not built with thread support");
}
#else
struct SDL_Semaphore
{
Uint32 count;
Uint32 waiters_count;
SDL_Mutex *count_lock;
SDL_Condition *count_nonzero;
};
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem;
sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
if (sem == NULL) {
SDL_OutOfMemory();
return NULL;
}
sem->count = initial_value;
sem->waiters_count = 0;
sem->count_lock = SDL_CreateMutex();
sem->count_nonzero = SDL_CreateCondition();
if (!sem->count_lock || !sem->count_nonzero) {
SDL_DestroySemaphore(sem);
return NULL;
}
return sem;
}
/* WARNING:
You cannot call this function when another thread is using the semaphore.
*/
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem) {
sem->count = 0xFFFFFFFF;
while (sem->waiters_count > 0) {
SDL_SignalCondition(sem->count_nonzero);
SDL_Delay(10);
}
SDL_DestroyCondition(sem->count_nonzero);
if (sem->count_lock) {
SDL_LockMutex(sem->count_lock);
SDL_UnlockMutex(sem->count_lock);
SDL_DestroyMutex(sem->count_lock);
}
SDL_free(sem);
}
}
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
int retval;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
/* A timeout of 0 is an easy case */
if (timeoutNS == 0) {
retval = SDL_MUTEX_TIMEDOUT;
SDL_LockMutex(sem->count_lock);
if (sem->count > 0) {
--sem->count;
retval = 0;
}
SDL_UnlockMutex(sem->count_lock);
return retval;
}
SDL_LockMutex(sem->count_lock);
++sem->waiters_count;
retval = 0;
while ((sem->count == 0) && (retval != SDL_MUTEX_TIMEDOUT)) {
retval = SDL_WaitConditionTimeoutNS(sem->count_nonzero,
sem->count_lock, timeoutNS);
}
--sem->waiters_count;
if (retval == 0) {
--sem->count;
}
SDL_UnlockMutex(sem->count_lock);
return retval;
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
Uint32 value;
value = 0;
if (sem) {
SDL_LockMutex(sem->count_lock);
value = sem->count;
SDL_UnlockMutex(sem->count_lock);
}
return value;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
SDL_LockMutex(sem->count_lock);
if (sem->waiters_count > 0) {
SDL_SignalCondition(sem->count_nonzero);
}
++sem->count;
SDL_UnlockMutex(sem->count_lock);
return 0;
}
#endif /* SDL_THREADS_DISABLED */

View File

@ -0,0 +1,61 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* Thread management routines for SDL */
#include "../SDL_systhread.h"
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
int SDL_SYS_CreateThread(SDL_Thread *thread,
pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread)
#else
int SDL_SYS_CreateThread(SDL_Thread *thread)
#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */
{
return SDL_SetError("Threads are not supported on this platform");
}
void SDL_SYS_SetupThread(const char *name)
{
return;
}
SDL_threadID SDL_ThreadID(void)
{
return 0;
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
return 0;
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
return;
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
return;
}

View File

@ -0,0 +1,24 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* Stub until we implement threads on this platform */
typedef int SYS_ThreadHandle;

View File

@ -0,0 +1,33 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include "../SDL_thread_c.h"
SDL_TLSData *SDL_SYS_GetTLSData(void)
{
return SDL_Generic_GetTLSData();
}
int SDL_SYS_SetTLSData(SDL_TLSData *data)
{
return SDL_Generic_SetTLSData(data);
}

View File

@ -0,0 +1,118 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_N3DS
/* An implementation of condition variables using libctru's CondVar */
#include "SDL_sysmutex_c.h"
struct SDL_Condition
{
CondVar cond_variable;
};
/* Create a condition variable */
SDL_Condition *SDL_CreateCondition(void)
{
SDL_Condition *cond = (SDL_Condition *)SDL_malloc(sizeof(SDL_Condition));
if (cond) {
CondVar_Init(&cond->cond_variable);
} else {
SDL_OutOfMemory();
}
return cond;
}
/* Destroy a condition variable */
void SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond) {
SDL_free(cond);
}
}
/* Restart one of the threads that are waiting on the condition variable */
int SDL_SignalCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
CondVar_Signal(&cond->cond_variable);
return 0;
}
/* Restart all threads that are waiting on the condition variable */
int SDL_BroadcastCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
CondVar_Broadcast(&cond->cond_variable);
return 0;
}
/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.
The mutex must be locked before entering this function!
The mutex is unlocked during the wait, and locked again after the wait.
Typical use:
Thread A:
SDL_LockMutex(lock);
while ( ! condition ) {
SDL_WaitCondition(cond, lock);
}
SDL_UnlockMutex(lock);
Thread B:
SDL_LockMutex(lock);
...
condition = true;
...
SDL_SignalCondition(cond);
SDL_UnlockMutex(lock);
*/
int SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
Result res;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
if (mutex == NULL) {
return SDL_InvalidParamError("mutex");
}
res = 0;
if (timeoutNS < 0) {
CondVar_Wait(&cond->cond_variable, &mutex->lock.lock);
} else {
res = CondVar_WaitTimeout(&cond->cond_variable, &mutex->lock.lock, timeoutNS);
}
return R_SUCCEEDED(res) ? 0 : SDL_MUTEX_TIMEDOUT;
}
#endif /* SDL_THREAD_N3DS */

View File

@ -0,0 +1,86 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_N3DS
/* An implementation of mutexes using libctru's RecursiveLock */
#include "SDL_sysmutex_c.h"
/* Create a mutex */
SDL_Mutex *SDL_CreateMutex(void)
{
SDL_Mutex *mutex;
/* Allocate mutex memory */
mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex));
if (mutex) {
RecursiveLock_Init(&mutex->lock);
} else {
SDL_OutOfMemory();
}
return mutex;
}
/* Free the mutex */
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
SDL_free(mutex);
}
}
/* Lock the mutex */
int SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (mutex == NULL) {
return 0;
}
RecursiveLock_Lock(&mutex->lock);
return 0;
}
/* try Lock the mutex */
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
if (mutex == NULL) {
return 0;
}
return RecursiveLock_TryLock(&mutex->lock);
}
/* Unlock the mutex */
int SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (mutex == NULL) {
return 0;
}
RecursiveLock_Unlock(&mutex->lock);
return 0;
}
#endif /* SDL_THREAD_N3DS */

View File

@ -0,0 +1,33 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_sysmutex_c_h_
#define SDL_sysmutex_c_h_
#include <3ds.h>
struct SDL_Mutex
{
RecursiveLock lock;
};
#endif /* SDL_sysmutex_c_h */

View File

@ -0,0 +1,118 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_N3DS
/* An implementation of semaphores using libctru's LightSemaphore */
#include <3ds.h>
int WaitOnSemaphoreFor(SDL_Semaphore *sem, Sint64 timeout);
struct SDL_Semaphore
{
LightSemaphore semaphore;
};
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem;
if (initial_value > SDL_MAX_SINT16) {
SDL_SetError("Initial semaphore value too high for this platform");
return NULL;
}
sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
if (sem == NULL) {
SDL_OutOfMemory();
return NULL;
}
LightSemaphore_Init(&sem->semaphore, initial_value, SDL_MAX_SINT16);
return sem;
}
/* WARNING:
You cannot call this function when another thread is using the semaphore.
*/
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
SDL_free(sem);
}
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeoutNS == SDL_MUTEX_MAXWAIT) {
LightSemaphore_Acquire(&sem->semaphore, 1);
return 0;
}
if (LightSemaphore_TryAcquire(&sem->semaphore, 1) != 0) {
return WaitOnSemaphoreFor(sem, timeoutNS);
}
return 0;
}
int WaitOnSemaphoreFor(SDL_Semaphore *sem, Sint64 timeout)
{
Uint64 stop_time = SDL_GetTicksNS() + timeout;
Uint64 current_time = SDL_GetTicksNS();
while (current_time < stop_time) {
if (LightSemaphore_TryAcquire(&sem->semaphore, 1) == 0) {
return 0;
}
/* 100 microseconds seems to be the sweet spot */
SDL_DelayNS(SDL_US_TO_NS(100));
current_time = SDL_GetTicksNS();
}
/* If we failed, yield to avoid starvation on busy waits */
SDL_DelayNS(1);
return SDL_MUTEX_TIMEDOUT;
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
return sem->semaphore.current_count;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
LightSemaphore_Release(&sem->semaphore, 1);
return 0;
}
#endif /* SDL_THREAD_N3DS */

View File

@ -0,0 +1,139 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_N3DS
/* Thread management routines for SDL */
#include "../SDL_systhread.h"
/* N3DS has very limited RAM (128MB), so we put a limit on thread stack size. */
#define N3DS_THREAD_STACK_SIZE_MAX (16 * 1024)
#define N3DS_THREAD_STACK_SIZE_DEFAULT (4 * 1024)
#define N3DS_THREAD_PRIORITY_LOW 0x3F /**< Minimum priority */
#define N3DS_THREAD_PRIORITY_MEDIUM 0x2F /**< Slightly higher than main thread (0x30) */
#define N3DS_THREAD_PRIORITY_HIGH 0x19 /**< High priority for non-video work */
#define N3DS_THREAD_PRIORITY_TIME_CRITICAL 0x18 /**< Highest priority */
static size_t GetStackSize(size_t requested_size);
static void ThreadEntry(void *arg)
{
SDL_RunThread((SDL_Thread *)arg);
threadExit(0);
}
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
#error "SDL_PASSED_BEGINTHREAD_ENDTHREAD is not supported on N3DS"
#endif
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
s32 priority;
size_t stack_size = GetStackSize(thread->stacksize);
svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
thread->handle = threadCreate(ThreadEntry,
thread,
stack_size,
priority,
-1,
false);
if (thread->handle == NULL) {
return SDL_SetError("Couldn't create thread");
}
return 0;
}
static size_t GetStackSize(size_t requested_size)
{
if (requested_size == 0) {
return N3DS_THREAD_STACK_SIZE_DEFAULT;
}
if (requested_size > N3DS_THREAD_STACK_SIZE_MAX) {
SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM,
"Requested a thread size of %zu,"
" falling back to the maximum supported of %zu\n",
requested_size,
N3DS_THREAD_STACK_SIZE_MAX);
requested_size = N3DS_THREAD_STACK_SIZE_MAX;
}
return requested_size;
}
void SDL_SYS_SetupThread(const char *name)
{
return;
}
SDL_threadID SDL_ThreadID(void)
{
u32 thread_ID = 0;
svcGetThreadId(&thread_ID, CUR_THREAD_HANDLE);
return (SDL_threadID)thread_ID;
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority sdl_priority)
{
s32 svc_priority;
switch (sdl_priority) {
case SDL_THREAD_PRIORITY_LOW:
svc_priority = N3DS_THREAD_PRIORITY_LOW;
break;
case SDL_THREAD_PRIORITY_NORMAL:
svc_priority = N3DS_THREAD_PRIORITY_MEDIUM;
break;
case SDL_THREAD_PRIORITY_HIGH:
svc_priority = N3DS_THREAD_PRIORITY_HIGH;
break;
case SDL_THREAD_PRIORITY_TIME_CRITICAL:
svc_priority = N3DS_THREAD_PRIORITY_TIME_CRITICAL;
break;
default:
svc_priority = N3DS_THREAD_PRIORITY_MEDIUM;
}
return (int)svcSetThreadPriority(CUR_THREAD_HANDLE, svc_priority);
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
Result res = threadJoin(thread->handle, U64_MAX);
/*
Detached threads can be waited on, but should NOT be cleaned manually
as it would result in a fatal error.
*/
if (R_SUCCEEDED(res) && SDL_AtomicGet(&thread->state) != SDL_THREAD_STATE_DETACHED) {
threadFree(thread->handle);
}
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
threadDetach(thread->handle);
}
#endif /* SDL_THREAD_N3DS */

View File

@ -0,0 +1,30 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_systhread_c_h_
#define SDL_systhread_c_h_
#include <3ds.h>
typedef Thread SYS_ThreadHandle;
#endif /* SDL_systhread_c_h_ */

View File

@ -0,0 +1,110 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* An implementation of mutexes using the Symbian API. */
#include <e32std.h>
#include "SDL_systhread_c.h"
struct SDL_Mutex
{
TInt handle;
};
extern TInt CreateUnique(TInt (*aFunc)(const TDesC &aName, TAny *, TAny *), TAny *, TAny *);
static TInt NewMutex(const TDesC &aName, TAny *aPtr1, TAny *)
{
return ((RMutex *)aPtr1)->CreateGlobal(aName);
}
/* Create a mutex */
SDL_Mutex *SDL_CreateMutex(void)
{
RMutex rmutex;
TInt status = CreateUnique(NewMutex, &rmutex, NULL);
if (status != KErrNone) {
SDL_SetError("Couldn't create mutex.");
return NULL;
}
SDL_Mutex *mutex = new /*(ELeave)*/ SDL_Mutex;
mutex->handle = rmutex.Handle();
return mutex;
}
/* Free the mutex */
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
RMutex rmutex;
rmutex.SetHandle(mutex->handle);
rmutex.Signal();
rmutex.Close();
delete (mutex);
mutex = NULL;
}
}
/* Lock the mutex */
int SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (mutex == NULL) {
return 0;
}
RMutex rmutex;
rmutex.SetHandle(mutex->handle);
rmutex.Wait();
return 0;
}
/* Try to lock the mutex */
#if 0
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
if (mutex == NULL)
{
return 0;
}
// Not yet implemented.
return 0;
}
#endif
/* Unlock the mutex */
int SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (mutex == NULL) {
return 0;
}
RMutex rmutex;
rmutex.SetHandle(mutex->handle);
rmutex.Signal();
return 0;
}

View File

@ -0,0 +1,160 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* An implementation of semaphores using the Symbian API. */
#include <e32std.h>
/* !!! FIXME: Should this be SDL_MUTEX_TIMEDOUT? */
#define SDL_MUTEX_TIMEOUT -2
struct SDL_Semaphore
{
TInt handle;
TInt count;
};
struct TInfo
{
TInfo(TInt aTime, TInt aHandle) : iTime(aTime), iHandle(aHandle), iVal(0) {}
TInt iTime;
TInt iHandle;
TInt iVal;
};
extern TInt CreateUnique(TInt (*aFunc)(const TDesC &aName, TAny *, TAny *), TAny *, TAny *);
static TBool RunThread(TAny *aInfo)
{
TInfo *info = STATIC_CAST(TInfo *, aInfo);
User::After(info->iTime);
RSemaphore sema;
sema.SetHandle(info->iHandle);
sema.Signal();
info->iVal = SDL_MUTEX_TIMEOUT;
return 0;
}
static TInt NewThread(const TDesC &aName, TAny *aPtr1, TAny *aPtr2)
{
return ((RThread *)(aPtr1))->Create(aName, RunThread, KDefaultStackSize, NULL, aPtr2);
}
static TInt NewSema(const TDesC &aName, TAny *aPtr1, TAny *aPtr2)
{
TInt value = *((TInt *)aPtr2);
return ((RSemaphore *)aPtr1)->CreateGlobal(aName, value);
}
static void WaitAll(SDL_Semaphore *sem)
{
RSemaphore sema;
sema.SetHandle(sem->handle);
sema.Wait();
while (sem->count < 0) {
sema.Wait();
}
}
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
RSemaphore s;
TInt status = CreateUnique(NewSema, &s, &initial_value);
if (status != KErrNone) {
SDL_SetError("Couldn't create semaphore");
}
SDL_Semaphore *sem = new /*(ELeave)*/ SDL_Semaphore;
sem->handle = s.Handle();
sem->count = initial_value;
return sem;
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem != NULL) {
RSemaphore sema;
sema.SetHandle(sem->handle);
sema.Signal(sema.Count());
sema.Close();
delete sem;
sem = NULL;
}
}
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeoutNS == 0) {
if (sem->count > 0) {
--sem->count;
return 0;
}
return SDL_MUTEX_TIMEOUT;
}
if (timeoutNS == SDL_MUTEX_MAXWAIT) {
WaitAll(sem);
return 0;
}
RThread thread;
TInfo *info = new (ELeave) TInfo((TInt)SDL_NS_TO_MS(timeoutNS), sem->handle);
TInt status = CreateUnique(NewThread, &thread, info);
if (status != KErrNone) {
return status;
}
thread.Resume();
WaitAll(sem);
if (thread.ExitType() == EExitPending) {
thread.Kill(SDL_MUTEX_TIMEOUT);
}
thread.Close();
return info->iVal;
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
return sem->count;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
sem->count++;
RSemaphore sema;
sema.SetHandle(sem->handle);
sema.Signal();
return 0;
}

View File

@ -0,0 +1,110 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_NGAGE
/* N-Gage thread management routines for SDL */
#include <e32std.h>
extern "C" {
#undef NULL
#include "../SDL_systhread.h"
#include "../SDL_thread_c.h"
};
static int object_count;
static int RunThread(TAny *data)
{
SDL_RunThread((SDL_Thread *)data);
return 0;
}
static TInt NewThread(const TDesC &aName, TAny *aPtr1, TAny *aPtr2)
{
return ((RThread *)(aPtr1))->Create(aName, RunThread, KDefaultStackSize, NULL, aPtr2);
}
int CreateUnique(TInt (*aFunc)(const TDesC &aName, TAny *, TAny *), TAny *aPtr1, TAny *aPtr2)
{
TBuf<16> name;
TInt status = KErrNone;
do {
object_count++;
name.Format(_L("SDL_%x"), object_count);
status = aFunc(name, aPtr1, aPtr2);
} while (status == KErrAlreadyExists);
return status;
}
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
RThread rthread;
TInt status = CreateUnique(NewThread, &rthread, thread);
if (status != KErrNone) {
delete (RThread *)thread->handle;
thread->handle = NULL;
return SDL_SetError("Not enough resources to create thread");
}
rthread.Resume();
thread->handle = rthread.Handle();
return 0;
}
void SDL_SYS_SetupThread(const char *name)
{
return;
}
SDL_threadID SDL_ThreadID(void)
{
RThread current;
TThreadId id = current.Id();
return id;
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
return 0;
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
RThread t;
t.Open(thread->threadid);
if (t.ExitReason() == EExitPending) {
TRequestStatus status;
t.Logon(status);
User::WaitForRequest(status);
}
t.Close();
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
return;
}
#endif /* SDL_THREAD_NGAGE */

View File

@ -0,0 +1,23 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
typedef int SYS_ThreadHandle;

View File

@ -0,0 +1,145 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_PS2
/* Semaphore functions for the PS2. */
#include <stdio.h>
#include <stdlib.h>
#include <timer_alarm.h>
#include <kernel.h>
struct SDL_Semaphore
{
s32 semid;
};
static void usercb(struct timer_alarm_t *alarm, void *arg)
{
iReleaseWaitThread((int)arg);
}
/* Create a semaphore */
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem;
ee_sema_t sema;
sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
if (sem != NULL) {
/* TODO: Figure out the limit on the maximum value. */
sema.init_count = initial_value;
sema.max_count = 255;
sema.option = 0;
sem->semid = CreateSema(&sema);
if (sem->semid < 0) {
SDL_SetError("Couldn't create semaphore");
SDL_free(sem);
sem = NULL;
}
} else {
SDL_OutOfMemory();
}
return sem;
}
/* Free the semaphore */
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem != NULL) {
if (sem->semid > 0) {
DeleteSema(sem->semid);
sem->semid = 0;
}
SDL_free(sem);
}
}
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
int ret;
struct timer_alarm_t alarm;
InitializeTimerAlarm(&alarm);
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeoutNS == 0) {
if (PollSema(sem->semid) < 0) {
return SDL_MUTEX_TIMEDOUT;
}
return 0;
}
if (timeoutNS != SDL_MUTEX_MAXWAIT) {
SetTimerAlarm(&alarm, MSec2TimerBusClock(SDL_NS_TO_MS(timeoutNS)), &usercb, (void *)GetThreadId());
}
ret = WaitSema(sem->semid);
StopTimerAlarm(&alarm);
if (ret < 0) {
return SDL_MUTEX_TIMEDOUT;
}
return 0; // Wait condition satisfied.
}
/* Returns the current count of the semaphore */
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
ee_sema_t info;
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
if (ReferSemaStatus(sem->semid, &info) >= 0) {
return info.count;
}
return 0;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
int res;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
res = SignalSema(sem->semid);
if (res < 0) {
return SDL_SetError("sceKernelSignalSema() failed");
}
return 0;
}
#endif /* SDL_THREAD_PS2 */

View File

@ -0,0 +1,135 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_PS2
/* PS2 thread management routines for SDL */
#include <stdio.h>
#include <stdlib.h>
#include "../SDL_systhread.h"
#include "../SDL_thread_c.h"
#include <kernel.h>
static void FinishThread(SDL_Thread *thread)
{
ee_thread_status_t info;
int res;
res = ReferThreadStatus(thread->handle, &info);
TerminateThread(thread->handle);
DeleteThread(thread->handle);
DeleteSema((int)thread->endfunc);
if (res > 0) {
SDL_free(info.stack);
}
}
static int childThread(void *arg)
{
SDL_Thread *thread = (SDL_Thread *)arg;
int res = thread->userfunc(thread->userdata);
SignalSema((int)thread->endfunc);
return res;
}
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
ee_thread_status_t status;
ee_thread_t eethread;
ee_sema_t sema;
size_t stack_size;
int priority = 32;
/* Set priority of new thread to the same as the current thread */
// status.size = sizeof(ee_thread_t);
if (ReferThreadStatus(GetThreadId(), &status) == 0) {
priority = status.current_priority;
}
stack_size = thread->stacksize ? ((int)thread->stacksize) : 0x1800;
/* Create EE Thread */
eethread.attr = 0;
eethread.option = 0;
eethread.func = &childThread;
eethread.stack = SDL_malloc(stack_size);
eethread.stack_size = stack_size;
eethread.gp_reg = &_gp;
eethread.initial_priority = priority;
thread->handle = CreateThread(&eethread);
if (thread->handle < 0) {
return SDL_SetError("CreateThread() failed");
}
// Prepare el semaphore for the ending function
sema.init_count = 0;
sema.max_count = 1;
sema.option = 0;
thread->endfunc = (void *)CreateSema(&sema);
return StartThread(thread->handle, thread);
}
void SDL_SYS_SetupThread(const char *name)
{
/* Do nothing. */
}
SDL_threadID SDL_ThreadID(void)
{
return (SDL_threadID)GetThreadId();
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
WaitSema((int)thread->endfunc);
ReleaseWaitThread(thread->handle);
FinishThread(thread);
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
/* Do nothing. */
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
int value;
if (priority == SDL_THREAD_PRIORITY_LOW) {
value = 111;
} else if (priority == SDL_THREAD_PRIORITY_HIGH) {
value = 32;
} else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
value = 16;
} else {
value = 50;
}
return ChangeThreadPriority(GetThreadId(), value);
}
#endif /* SDL_THREAD_PS2 */

View File

@ -0,0 +1,24 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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 <stdint.h>
typedef int32_t SYS_ThreadHandle;

View File

@ -0,0 +1,204 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_PSP
/* An implementation of condition variables using semaphores and mutexes */
/*
This implementation borrows heavily from the BeOS condition variable
implementation, written by Christopher Tate and Owen Smith. Thanks!
*/
struct SDL_Condition
{
SDL_Mutex *lock;
int waiting;
int signals;
SDL_Semaphore *wait_sem;
SDL_Semaphore *wait_done;
};
/* Create a condition variable */
SDL_Condition *SDL_CreateCondition(void)
{
SDL_Condition *cond;
cond = (SDL_Condition *)SDL_malloc(sizeof(SDL_Condition));
if (cond) {
cond->lock = SDL_CreateMutex();
cond->wait_sem = SDL_CreateSemaphore(0);
cond->wait_done = SDL_CreateSemaphore(0);
cond->waiting = cond->signals = 0;
if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
SDL_DestroyCondition(cond);
cond = NULL;
}
} else {
SDL_OutOfMemory();
}
return cond;
}
/* Destroy a condition variable */
void SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond) {
if (cond->wait_sem) {
SDL_DestroySemaphore(cond->wait_sem);
}
if (cond->wait_done) {
SDL_DestroySemaphore(cond->wait_done);
}
if (cond->lock) {
SDL_DestroyMutex(cond->lock);
}
SDL_free(cond);
}
}
/* Restart one of the threads that are waiting on the condition variable */
int SDL_SignalCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
++cond->signals;
SDL_PostSemaphore(cond->wait_sem);
SDL_UnlockMutex(cond->lock);
SDL_WaitSemaphore(cond->wait_done);
} else {
SDL_UnlockMutex(cond->lock);
}
return 0;
}
/* Restart all threads that are waiting on the condition variable */
int SDL_BroadcastCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
int i, num_waiting;
num_waiting = (cond->waiting - cond->signals);
cond->signals = cond->waiting;
for (i = 0; i < num_waiting; ++i) {
SDL_PostSemaphore(cond->wait_sem);
}
/* Now all released threads are blocked here, waiting for us.
Collect them all (and win fabulous prizes!) :-)
*/
SDL_UnlockMutex(cond->lock);
for (i = 0; i < num_waiting; ++i) {
SDL_WaitSemaphore(cond->wait_done);
}
} else {
SDL_UnlockMutex(cond->lock);
}
return 0;
}
/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.
The mutex must be locked before entering this function!
The mutex is unlocked during the wait, and locked again after the wait.
Typical use:
Thread A:
SDL_LockMutex(lock);
while ( ! condition ) {
SDL_WaitCondition(cond, lock);
}
SDL_UnlockMutex(lock);
Thread B:
SDL_LockMutex(lock);
...
condition = true;
...
SDL_SignalCondition(cond);
SDL_UnlockMutex(lock);
*/
int SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
int retval;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* Obtain the protection mutex, and increment the number of waiters.
This allows the signal mechanism to only perform a signal if there
are waiting threads.
*/
SDL_LockMutex(cond->lock);
++cond->waiting;
SDL_UnlockMutex(cond->lock);
/* Unlock the mutex, as is required by condition variable semantics */
SDL_UnlockMutex(mutex);
/* Wait for a signal */
retval = SDL_WaitSemaphoreTimeout(cond->wait_sem, timeoutNS);
/* Let the signaler know we have completed the wait, otherwise
the signaler can race ahead and get the condition semaphore
if we are stopped between the mutex unlock and semaphore wait,
giving a deadlock. See the following URL for details:
http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
*/
SDL_LockMutex(cond->lock);
if (cond->signals > 0) {
/* If we timed out, we need to eat a condition signal */
if (retval > 0) {
SDL_WaitSemaphore(cond->wait_sem);
}
/* We always notify the signal thread that we are done */
SDL_PostSemaphore(cond->wait_done);
/* Signal handshake complete */
--cond->signals;
}
--cond->waiting;
SDL_UnlockMutex(cond->lock);
/* Lock the mutex, as is required by condition variable semantics */
SDL_LockMutex(mutex);
return retval;
}
#endif /* SDL_THREAD_PSP */

View File

@ -0,0 +1,145 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_PSP
/* An implementation of mutexes using semaphores */
#include "SDL_systhread_c.h"
#include <pspthreadman.h>
#include <pspkerror.h>
#define SCE_KERNEL_MUTEX_ATTR_RECURSIVE 0x0200U
struct SDL_Mutex
{
SceLwMutexWorkarea lock;
};
/* Create a mutex */
SDL_Mutex *SDL_CreateMutex(void)
{
SDL_Mutex *mutex = NULL;
SceInt32 res = 0;
/* Allocate mutex memory */
mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex));
if (mutex) {
res = sceKernelCreateLwMutex(
&mutex->lock,
"SDL mutex",
SCE_KERNEL_MUTEX_ATTR_RECURSIVE,
0,
NULL);
if (res < 0) {
SDL_SetError("Error trying to create mutex: %lx", res);
}
} else {
SDL_OutOfMemory();
}
return mutex;
}
/* Free the mutex */
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
sceKernelDeleteLwMutex(&mutex->lock);
SDL_free(mutex);
}
}
/* Lock the mutex */
int SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
SceInt32 res = 0;
if (mutex == NULL) {
return 0;
}
res = sceKernelLockLwMutex(&mutex->lock, 1, NULL);
if (res != SCE_KERNEL_ERROR_OK) {
return SDL_SetError("Error trying to lock mutex: %lx", res);
}
return 0;
#endif /* SDL_THREADS_DISABLED */
}
/* Try to lock the mutex */
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
SceInt32 res = 0;
if (mutex == NULL) {
return 0;
}
res = sceKernelTryLockLwMutex(&mutex->lock, 1);
switch (res) {
case SCE_KERNEL_ERROR_OK:
return 0;
break;
case SCE_KERNEL_ERROR_WAIT_TIMEOUT:
return SDL_MUTEX_TIMEDOUT;
break;
default:
return SDL_SetError("Error trying to lock mutex: %lx", res);
break;
}
return -1;
#endif /* SDL_THREADS_DISABLED */
}
/* Unlock the mutex */
int SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
SceInt32 res = 0;
if (mutex == NULL) {
return 0;
}
res = sceKernelUnlockLwMutex(&mutex->lock, 1);
if (res != 0) {
return SDL_SetError("Error trying to unlock mutex: %lx", res);
}
return 0;
#endif /* SDL_THREADS_DISABLED */
}
#endif /* SDL_THREAD_PSP */

View File

@ -0,0 +1,21 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"

View File

@ -0,0 +1,145 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_PSP
/* Semaphore functions for the PSP. */
#include <stdio.h>
#include <stdlib.h>
#include <pspthreadman.h>
#include <pspkerror.h>
struct SDL_Semaphore
{
SceUID semid;
};
/* Create a semaphore */
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem;
sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
if (sem != NULL) {
/* TODO: Figure out the limit on the maximum value. */
sem->semid = sceKernelCreateSema("SDL sema", 0, initial_value, 255, NULL);
if (sem->semid < 0) {
SDL_SetError("Couldn't create semaphore");
SDL_free(sem);
sem = NULL;
}
} else {
SDL_OutOfMemory();
}
return sem;
}
/* Free the semaphore */
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem != NULL) {
if (sem->semid > 0) {
sceKernelDeleteSema(sem->semid);
sem->semid = 0;
}
SDL_free(sem);
}
}
/* TODO: This routine is a bit overloaded.
* If the timeout is 0 then just poll the semaphore; if it's SDL_MUTEX_MAXWAIT, pass
* NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout
* is specified, convert it to microseconds. */
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
SceUInt timeoutUS;
SceUInt *pTimeout;
int res;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeoutNS == 0) {
res = sceKernelPollSema(sem->semid, 1);
if (res < 0) {
return SDL_MUTEX_TIMEDOUT;
}
return 0;
}
if (timeoutNS < 0) {
pTimeout = NULL;
} else {
timeoutUS = (SceUInt)SDL_NS_TO_US(timeoutNS); /* Convert to microseconds. */
pTimeout = &timeoutUS;
}
res = sceKernelWaitSema(sem->semid, 1, pTimeout);
switch (res) {
case SCE_KERNEL_ERROR_OK:
return 0;
case SCE_KERNEL_ERROR_WAIT_TIMEOUT:
return SDL_MUTEX_TIMEDOUT;
default:
return SDL_SetError("sceKernelWaitSema() failed");
}
}
/* Returns the current count of the semaphore */
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
SceKernelSemaInfo info;
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
if (sceKernelReferSemaStatus(sem->semid, &info) >= 0) {
return info.currentCount;
}
return 0;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
int res;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
res = sceKernelSignalSema(sem->semid, 1);
if (res < 0) {
return SDL_SetError("sceKernelSignalSema() failed");
}
return 0;
}
#endif /* SDL_THREAD_PSP */

View File

@ -0,0 +1,107 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_PSP
/* PSP thread management routines for SDL */
#include <stdio.h>
#include <stdlib.h>
#include "../SDL_systhread.h"
#include "../SDL_thread_c.h"
#include <pspkerneltypes.h>
#include <pspthreadman.h>
static int ThreadEntry(SceSize args, void *argp)
{
SDL_RunThread(*(SDL_Thread **)argp);
return 0;
}
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
SceKernelThreadInfo status;
int priority = 32;
/* Set priority of new thread to the same as the current thread */
status.size = sizeof(SceKernelThreadInfo);
if (sceKernelReferThreadStatus(sceKernelGetThreadId(), &status) == 0) {
priority = status.currentPriority;
}
thread->handle = sceKernelCreateThread(thread->name, ThreadEntry,
priority, thread->stacksize ? ((int)thread->stacksize) : 0x8000,
PSP_THREAD_ATTR_VFPU, NULL);
if (thread->handle < 0) {
return SDL_SetError("sceKernelCreateThread() failed");
}
sceKernelStartThread(thread->handle, 4, &thread);
return 0;
}
void SDL_SYS_SetupThread(const char *name)
{
/* Do nothing. */
}
SDL_threadID SDL_ThreadID(void)
{
return (SDL_threadID)sceKernelGetThreadId();
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
sceKernelWaitThreadEnd(thread->handle, NULL);
sceKernelDeleteThread(thread->handle);
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
/* !!! FIXME: is this correct? */
sceKernelDeleteThread(thread->handle);
}
void SDL_SYS_KillThread(SDL_Thread *thread)
{
sceKernelTerminateDeleteThread(thread->handle);
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
int value;
if (priority == SDL_THREAD_PRIORITY_LOW) {
value = 111;
} else if (priority == SDL_THREAD_PRIORITY_HIGH) {
value = 32;
} else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
value = 16;
} else {
value = 50;
}
return sceKernelChangeThreadPriority(sceKernelGetThreadId(), value);
}
#endif /* SDL_THREAD_PSP */

View File

@ -0,0 +1,24 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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 <pspkerneltypes.h>
typedef SceUID SYS_ThreadHandle;

View File

@ -0,0 +1,143 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include "SDL_sysmutex_c.h"
struct SDL_Condition
{
pthread_cond_t cond;
};
/* Create a condition variable */
SDL_Condition *SDL_CreateCondition(void)
{
SDL_Condition *cond;
cond = (SDL_Condition *)SDL_malloc(sizeof(SDL_Condition));
if (cond) {
if (pthread_cond_init(&cond->cond, NULL) != 0) {
SDL_SetError("pthread_cond_init() failed");
SDL_free(cond);
cond = NULL;
}
}
return cond;
}
/* Destroy a condition variable */
void SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond) {
pthread_cond_destroy(&cond->cond);
SDL_free(cond);
}
}
/* Restart one of the threads that are waiting on the condition variable */
int SDL_SignalCondition(SDL_Condition *cond)
{
int retval;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
retval = 0;
if (pthread_cond_signal(&cond->cond) != 0) {
return SDL_SetError("pthread_cond_signal() failed");
}
return retval;
}
/* Restart all threads that are waiting on the condition variable */
int SDL_BroadcastCondition(SDL_Condition *cond)
{
int retval;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
retval = 0;
if (pthread_cond_broadcast(&cond->cond) != 0) {
return SDL_SetError("pthread_cond_broadcast() failed");
}
return retval;
}
int SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
int retval;
#ifndef HAVE_CLOCK_GETTIME
struct timeval delta;
#endif
struct timespec abstime;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
if (timeoutNS < 0) {
if (pthread_cond_wait(&cond->cond, &mutex->id) != 0) {
return SDL_SetError("pthread_cond_wait() failed");
}
return 0;
}
#ifdef HAVE_CLOCK_GETTIME
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += (timeoutNS / SDL_NS_PER_SECOND);
abstime.tv_nsec += (timeoutNS % SDL_NS_PER_SECOND);
#else
gettimeofday(&delta, NULL);
abstime.tv_sec = delta.tv_sec + (timeoutNS / SDL_NS_PER_SECOND);
abstime.tv_nsec = SDL_US_TO_NS(delta.tv_usec) + (timeoutNS % SDL_NS_PER_SECOND);
#endif
while (abstime.tv_nsec > 1000000000) {
abstime.tv_sec += 1;
abstime.tv_nsec -= 1000000000;
}
tryagain:
retval = pthread_cond_timedwait(&cond->cond, &mutex->id, &abstime);
switch (retval) {
case EINTR:
goto tryagain;
/* break; -Wunreachable-code-break */
case ETIMEDOUT:
retval = SDL_MUTEX_TIMEDOUT;
break;
case 0:
break;
default:
retval = SDL_SetError("pthread_cond_timedwait() failed");
}
return retval;
}

View File

@ -0,0 +1,175 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <errno.h>
#include <pthread.h>
#include "SDL_sysmutex_c.h"
SDL_Mutex *SDL_CreateMutex(void)
{
SDL_Mutex *mutex;
pthread_mutexattr_t attr;
/* Allocate the structure */
mutex = (SDL_Mutex *)SDL_calloc(1, sizeof(*mutex));
if (mutex) {
pthread_mutexattr_init(&attr);
#ifdef SDL_THREAD_PTHREAD_RECURSIVE_MUTEX
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#elif defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP)
pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
#else
/* No extra attributes necessary */
#endif
if (pthread_mutex_init(&mutex->id, &attr) != 0) {
SDL_SetError("pthread_mutex_init() failed");
SDL_free(mutex);
mutex = NULL;
}
} else {
SDL_OutOfMemory();
}
return mutex;
}
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
pthread_mutex_destroy(&mutex->id);
SDL_free(mutex);
}
}
/* Lock the mutex */
int SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
#ifdef FAKE_RECURSIVE_MUTEX
pthread_t this_thread;
#endif
if (mutex == NULL) {
return 0;
}
#ifdef FAKE_RECURSIVE_MUTEX
this_thread = pthread_self();
if (mutex->owner == this_thread) {
++mutex->recursive;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
if (pthread_mutex_lock(&mutex->id) == 0) {
mutex->owner = this_thread;
mutex->recursive = 0;
} else {
return SDL_SetError("pthread_mutex_lock() failed");
}
}
#else
if (pthread_mutex_lock(&mutex->id) != 0) {
return SDL_SetError("pthread_mutex_lock() failed");
}
#endif
return 0;
}
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
int retval;
int result;
#ifdef FAKE_RECURSIVE_MUTEX
pthread_t this_thread;
#endif
if (mutex == NULL) {
return 0;
}
retval = 0;
#ifdef FAKE_RECURSIVE_MUTEX
this_thread = pthread_self();
if (mutex->owner == this_thread) {
++mutex->recursive;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
result = pthread_mutex_trylock(&mutex->id);
if (result == 0) {
mutex->owner = this_thread;
mutex->recursive = 0;
} else if (result == EBUSY) {
retval = SDL_MUTEX_TIMEDOUT;
} else {
retval = SDL_SetError("pthread_mutex_trylock() failed");
}
}
#else
result = pthread_mutex_trylock(&mutex->id);
if (result != 0) {
if (result == EBUSY) {
retval = SDL_MUTEX_TIMEDOUT;
} else {
retval = SDL_SetError("pthread_mutex_trylock() failed");
}
}
#endif
return retval;
}
int SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (mutex == NULL) {
return 0;
}
#ifdef FAKE_RECURSIVE_MUTEX
/* We can only unlock the mutex if we own it */
if (pthread_self() == mutex->owner) {
if (mutex->recursive) {
--mutex->recursive;
} else {
/* The order of operations is important.
First reset the owner so another thread doesn't lock
the mutex and set the ownership before we reset it,
then release the lock semaphore.
*/
mutex->owner = 0;
pthread_mutex_unlock(&mutex->id);
}
} else {
return SDL_SetError("mutex not owned by this thread");
}
#else
if (pthread_mutex_unlock(&mutex->id) != 0) {
return SDL_SetError("pthread_mutex_unlock() failed");
}
#endif /* FAKE_RECURSIVE_MUTEX */
return 0;
}

View File

@ -0,0 +1,40 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_mutex_c_h_
#define SDL_mutex_c_h_
#if !(defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX) || \
defined(SDL_THREAD_PTHREAD_RECURSIVE_MUTEX_NP))
#define FAKE_RECURSIVE_MUTEX
#endif
struct SDL_Mutex
{
pthread_mutex_t id;
#ifdef FAKE_RECURSIVE_MUTEX
int recursive;
pthread_t owner;
#endif
};
#endif /* SDL_mutex_c_h_ */

View File

@ -0,0 +1,126 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <errno.h>
#include <pthread.h>
struct SDL_RWLock
{
pthread_rwlock_t id;
};
SDL_RWLock *SDL_CreateRWLock(void)
{
SDL_RWLock *rwlock;
/* Allocate the structure */
rwlock = (SDL_RWLock *)SDL_calloc(1, sizeof(*rwlock));
if (rwlock) {
if (pthread_rwlock_init(&rwlock->id, NULL) != 0) {
SDL_SetError("pthread_rwlock_init() failed");
SDL_free(rwlock);
rwlock = NULL;
}
} else {
SDL_OutOfMemory();
}
return rwlock;
}
void SDL_DestroyRWLock(SDL_RWLock *rwlock)
{
if (rwlock) {
pthread_rwlock_destroy(&rwlock->id);
SDL_free(rwlock);
}
}
int SDL_LockRWLockForReading(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (rwlock == NULL) {
return SDL_InvalidParamError("rwlock");
} else if (pthread_rwlock_rdlock(&rwlock->id) != 0) {
return SDL_SetError("pthread_rwlock_rdlock() failed");
}
return 0;
}
int SDL_LockRWLockForWriting(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (rwlock == NULL) {
return SDL_InvalidParamError("rwlock");
} else if (pthread_rwlock_wrlock(&rwlock->id) != 0) {
return SDL_SetError("pthread_rwlock_wrlock() failed");
}
return 0;
}
int SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
{
int retval = 0;
if (rwlock == NULL) {
retval = SDL_InvalidParamError("rwlock");
} else {
const int result = pthread_rwlock_tryrdlock(&rwlock->id);
if (result != 0) {
if (result == EBUSY) {
retval = SDL_RWLOCK_TIMEDOUT;
} else {
retval = SDL_SetError("pthread_rwlock_tryrdlock() failed");
}
}
}
return retval;
}
int SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
{
int retval = 0;
if (rwlock == NULL) {
retval = SDL_InvalidParamError("rwlock");
} else {
const int result = pthread_rwlock_trywrlock(&rwlock->id);
if (result != 0) {
if (result == EBUSY) {
retval = SDL_RWLOCK_TIMEDOUT;
} else {
retval = SDL_SetError("pthread_rwlock_tryrdlock() failed");
}
}
}
return retval;
}
int SDL_UnlockRWLock(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (rwlock == NULL) {
return SDL_InvalidParamError("rwlock");
} else if (pthread_rwlock_unlock(&rwlock->id) != 0) {
return SDL_SetError("pthread_rwlock_unlock() failed");
}
return 0;
}

View File

@ -0,0 +1,182 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/time.h>
#include <time.h>
/* Wrapper around POSIX 1003.1b semaphores */
#if defined(__MACOS__) || defined(__IOS__)
/* macOS doesn't support sem_getvalue() as of version 10.4 */
#include "../generic/SDL_syssem.c"
#else
struct SDL_Semaphore
{
sem_t sem;
};
/* Create a semaphore, initialized with value */
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem = (SDL_Semaphore *)SDL_malloc(sizeof(SDL_Semaphore));
if (sem != NULL) {
if (sem_init(&sem->sem, 0, initial_value) < 0) {
SDL_SetError("sem_init() failed");
SDL_free(sem);
sem = NULL;
}
} else {
SDL_OutOfMemory();
}
return sem;
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem != NULL) {
sem_destroy(&sem->sem);
SDL_free(sem);
}
}
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
int retval = 0;
#ifdef HAVE_SEM_TIMEDWAIT
#ifndef HAVE_CLOCK_GETTIME
struct timeval now;
#endif
struct timespec ts_timeout;
#else
Uint64 end;
#endif
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
/* Try the easy cases first */
if (timeoutNS == 0) {
retval = SDL_MUTEX_TIMEDOUT;
if (sem_trywait(&sem->sem) == 0) {
retval = 0;
}
return retval;
}
if (timeoutNS < 0) {
do {
retval = sem_wait(&sem->sem);
} while (retval < 0 && errno == EINTR);
if (retval < 0) {
retval = SDL_SetError("sem_wait() failed");
}
return retval;
}
#ifdef HAVE_SEM_TIMEDWAIT
/* Setup the timeout. sem_timedwait doesn't wait for
* a lapse of time, but until we reach a certain time.
* This time is now plus the timeout.
*/
#ifdef HAVE_CLOCK_GETTIME
clock_gettime(CLOCK_REALTIME, &ts_timeout);
/* Add our timeout to current time */
ts_timeout.tv_sec += (timeoutNS / SDL_NS_PER_SECOND);
ts_timeout.tv_nsec += (timeoutNS % SDL_NS_PER_SECOND);
#else
gettimeofday(&now, NULL);
/* Add our timeout to current time */
ts_timeout.tv_sec = now.tv_sec + (timeoutNS / SDL_NS_PER_SECOND);
ts_timeout.tv_nsec = SDL_US_TO_NS(now.tv_usec) + (timeoutNS % SDL_NS_PER_SECOND);
#endif
/* Wrap the second if needed */
while (ts_timeout.tv_nsec > 1000000000) {
ts_timeout.tv_sec += 1;
ts_timeout.tv_nsec -= 1000000000;
}
/* Wait. */
do {
retval = sem_timedwait(&sem->sem, &ts_timeout);
} while (retval < 0 && errno == EINTR);
if (retval < 0) {
if (errno == ETIMEDOUT) {
retval = SDL_MUTEX_TIMEDOUT;
} else {
SDL_SetError("sem_timedwait returned an error: %s", strerror(errno));
}
}
#else
end = SDL_GetTicksNS() + timeoutNS;
while (sem_trywait(&sem->sem) != 0) {
if (SDL_GetTicksNS() >= end) {
retval = SDL_MUTEX_TIMEDOUT;
break;
}
SDL_DelayNS(100);
}
#endif /* HAVE_SEM_TIMEDWAIT */
return retval;
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
int ret = 0;
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
sem_getvalue(&sem->sem, &ret);
if (ret < 0) {
ret = 0;
}
return (Uint32)ret;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
int retval;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
retval = sem_post(&sem->sem);
if (retval < 0) {
SDL_SetError("sem_post() failed");
}
return retval;
}
#endif /* __MACOS__ */

View File

@ -0,0 +1,283 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <pthread.h>
#ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h>
#endif
#include <signal.h>
#include <errno.h>
#ifdef __LINUX__
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "../../core/linux/SDL_dbus.h"
#endif /* __LINUX__ */
#if (defined(__LINUX__) || defined(__MACOS__) || defined(__IOS__)) && defined(HAVE_DLOPEN)
#include <dlfcn.h>
#ifndef RTLD_DEFAULT
#define RTLD_DEFAULT NULL
#endif
#endif
#include "../SDL_thread_c.h"
#include "../SDL_systhread.h"
#ifdef __ANDROID__
#include "../../core/android/SDL_android.h"
#endif
#ifdef __HAIKU__
#include <kernel/OS.h>
#endif
/* List of signals to mask in the subthreads */
static const int sig_list[] = {
SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGWINCH,
SIGVTALRM, SIGPROF, 0
};
static void *RunThread(void *data)
{
#ifdef __ANDROID__
Android_JNI_SetupThread();
#endif
SDL_RunThread((SDL_Thread *)data);
return NULL;
}
#if (defined(__MACOS__) || defined(__IOS__)) && defined(HAVE_DLOPEN)
static SDL_bool checked_setname = SDL_FALSE;
static int (*ppthread_setname_np)(const char *) = NULL;
#elif defined(__LINUX__) && defined(HAVE_DLOPEN)
static SDL_bool checked_setname = SDL_FALSE;
static int (*ppthread_setname_np)(pthread_t, const char *) = NULL;
#endif
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
pthread_attr_t type;
/* do this here before any threads exist, so there's no race condition. */
#if (defined(__MACOS__) || defined(__IOS__) || defined(__LINUX__)) && defined(HAVE_DLOPEN)
if (!checked_setname) {
void *fn = dlsym(RTLD_DEFAULT, "pthread_setname_np");
#if defined(__MACOS__) || defined(__IOS__)
ppthread_setname_np = (int (*)(const char *))fn;
#elif defined(__LINUX__)
ppthread_setname_np = (int (*)(pthread_t, const char *))fn;
#endif
checked_setname = SDL_TRUE;
}
#endif
/* Set the thread attributes */
if (pthread_attr_init(&type) != 0) {
return SDL_SetError("Couldn't initialize pthread attributes");
}
pthread_attr_setdetachstate(&type, PTHREAD_CREATE_JOINABLE);
/* Set caller-requested stack size. Otherwise: use the system default. */
if (thread->stacksize) {
pthread_attr_setstacksize(&type, thread->stacksize);
}
/* Create the thread and go! */
if (pthread_create(&thread->handle, &type, RunThread, thread) != 0) {
return SDL_SetError("Not enough resources to create thread");
}
return 0;
}
void SDL_SYS_SetupThread(const char *name)
{
int i;
sigset_t mask;
if (name != NULL) {
#if (defined(__MACOS__) || defined(__IOS__) || defined(__LINUX__)) && defined(HAVE_DLOPEN)
SDL_assert(checked_setname);
if (ppthread_setname_np != NULL) {
#if defined(__MACOS__) || defined(__IOS__)
ppthread_setname_np(name);
#elif defined(__LINUX__)
if (ppthread_setname_np(pthread_self(), name) == ERANGE) {
char namebuf[16]; /* Limited to 16 char */
SDL_strlcpy(namebuf, name, sizeof(namebuf));
ppthread_setname_np(pthread_self(), namebuf);
}
#endif
}
#elif defined(HAVE_PTHREAD_SETNAME_NP)
#ifdef __NETBSD__
pthread_setname_np(pthread_self(), "%s", name);
#else
if (pthread_setname_np(pthread_self(), name) == ERANGE) {
char namebuf[16]; /* Limited to 16 char */
SDL_strlcpy(namebuf, name, sizeof(namebuf));
pthread_setname_np(pthread_self(), namebuf);
}
#endif
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
pthread_set_name_np(pthread_self(), name);
#elif defined(__HAIKU__)
/* The docs say the thread name can't be longer than B_OS_NAME_LENGTH. */
char namebuf[B_OS_NAME_LENGTH];
SDL_strlcpy(namebuf, name, sizeof(namebuf));
rename_thread(find_thread(NULL), namebuf);
#endif
}
/* Mask asynchronous signals for this thread */
sigemptyset(&mask);
for (i = 0; sig_list[i]; ++i) {
sigaddset(&mask, sig_list[i]);
}
pthread_sigmask(SIG_BLOCK, &mask, 0);
#ifdef PTHREAD_CANCEL_ASYNCHRONOUS
/* Allow ourselves to be asynchronously cancelled */
{
int oldstate;
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
}
#endif
}
SDL_threadID SDL_ThreadID(void)
{
return (SDL_threadID)pthread_self();
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
#ifdef __RISCOS__
/* FIXME: Setting thread priority does not seem to be supported */
return 0;
#else
struct sched_param sched;
int policy;
int pri_policy;
pthread_t thread = pthread_self();
const char *policyhint = SDL_GetHint(SDL_HINT_THREAD_PRIORITY_POLICY);
const SDL_bool timecritical_realtime_hint = SDL_GetHintBoolean(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, SDL_FALSE);
if (pthread_getschedparam(thread, &policy, &sched) != 0) {
return SDL_SetError("pthread_getschedparam() failed");
}
/* Higher priority levels may require changing the pthread scheduler policy
* for the thread. SDL will make such changes by default but there is
* also a hint allowing that behavior to be overridden. */
switch (priority) {
case SDL_THREAD_PRIORITY_LOW:
case SDL_THREAD_PRIORITY_NORMAL:
pri_policy = SCHED_OTHER;
break;
case SDL_THREAD_PRIORITY_HIGH:
case SDL_THREAD_PRIORITY_TIME_CRITICAL:
#if defined(__MACOS__) || defined(__IOS__) || defined(__TVOS__)
/* Apple requires SCHED_RR for high priority threads */
pri_policy = SCHED_RR;
break;
#else
pri_policy = SCHED_OTHER;
break;
#endif
default:
pri_policy = policy;
break;
}
if (timecritical_realtime_hint && priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
pri_policy = SCHED_RR;
}
if (policyhint) {
if (SDL_strcmp(policyhint, "current") == 0) {
/* Leave current thread scheduler policy unchanged */
} else if (SDL_strcmp(policyhint, "other") == 0) {
policy = SCHED_OTHER;
} else if (SDL_strcmp(policyhint, "rr") == 0) {
policy = SCHED_RR;
} else if (SDL_strcmp(policyhint, "fifo") == 0) {
policy = SCHED_FIFO;
} else {
policy = pri_policy;
}
} else {
policy = pri_policy;
}
#ifdef __LINUX__
{
pid_t linuxTid = syscall(SYS_gettid);
return SDL_LinuxSetThreadPriorityAndPolicy(linuxTid, priority, policy);
}
#else
if (priority == SDL_THREAD_PRIORITY_LOW) {
sched.sched_priority = sched_get_priority_min(policy);
} else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
sched.sched_priority = sched_get_priority_max(policy);
} else {
int min_priority = sched_get_priority_min(policy);
int max_priority = sched_get_priority_max(policy);
#if defined(__MACOS__) || defined(__IOS__) || defined(__TVOS__)
if (min_priority == 15 && max_priority == 47) {
/* Apple has a specific set of thread priorities */
if (priority == SDL_THREAD_PRIORITY_HIGH) {
sched.sched_priority = 45;
} else {
sched.sched_priority = 37;
}
} else
#endif /* __MACOS__ || __IOS__ || __TVOS__ */
{
sched.sched_priority = (min_priority + (max_priority - min_priority) / 2);
if (priority == SDL_THREAD_PRIORITY_HIGH) {
sched.sched_priority += ((max_priority - min_priority) / 4);
}
}
}
if (pthread_setschedparam(thread, policy, &sched) != 0) {
return SDL_SetError("pthread_setschedparam() failed");
}
return 0;
#endif /* linux */
#endif /* #if __RISCOS__ */
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
pthread_join(thread->handle, 0);
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
pthread_detach(thread->handle);
}

View File

@ -0,0 +1,25 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <pthread.h>
typedef pthread_t SYS_ThreadHandle;

View File

@ -0,0 +1,64 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include "../SDL_systhread.h"
#include "../SDL_thread_c.h"
#include <pthread.h>
#define INVALID_PTHREAD_KEY ((pthread_key_t)-1)
static pthread_key_t thread_local_storage = INVALID_PTHREAD_KEY;
static SDL_bool generic_local_storage = SDL_FALSE;
SDL_TLSData *SDL_SYS_GetTLSData(void)
{
if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) {
static SDL_SpinLock lock;
SDL_AtomicLock(&lock);
if (thread_local_storage == INVALID_PTHREAD_KEY && !generic_local_storage) {
pthread_key_t storage;
if (pthread_key_create(&storage, NULL) == 0) {
SDL_MemoryBarrierRelease();
thread_local_storage = storage;
} else {
generic_local_storage = SDL_TRUE;
}
}
SDL_AtomicUnlock(&lock);
}
if (generic_local_storage) {
return SDL_Generic_GetTLSData();
}
SDL_MemoryBarrierAcquire();
return (SDL_TLSData *)pthread_getspecific(thread_local_storage);
}
int SDL_SYS_SetTLSData(SDL_TLSData *data)
{
if (generic_local_storage) {
return SDL_Generic_SetTLSData(data);
}
if (pthread_setspecific(thread_local_storage, data) != 0) {
return SDL_SetError("pthread_setspecific() failed");
}
return 0;
}

View File

@ -0,0 +1,141 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
extern "C" {
}
#include <chrono>
#include <condition_variable>
#include <ratio>
#include <system_error>
#include "SDL_sysmutex_c.h"
struct SDL_Condition
{
std::condition_variable_any cpp_cond;
};
/* Create a condition variable */
extern "C" SDL_Condition *
SDL_CreateCondition(void)
{
/* Allocate and initialize the condition variable */
try {
SDL_Condition *cond = new SDL_Condition;
return cond;
} catch (std::system_error &ex) {
SDL_SetError("unable to create a C++ condition variable: code=%d; %s", ex.code(), ex.what());
return NULL;
} catch (std::bad_alloc &) {
SDL_OutOfMemory();
return NULL;
}
}
/* Destroy a condition variable */
extern "C" void
SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond != NULL) {
delete cond;
}
}
/* Restart one of the threads that are waiting on the condition variable */
extern "C" int
SDL_SignalCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
cond->cpp_cond.notify_one();
return 0;
}
/* Restart all threads that are waiting on the condition variable */
extern "C" int
SDL_BroadcastCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
cond->cpp_cond.notify_all();
return 0;
}
/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.
The mutex must be locked before entering this function!
The mutex is unlocked during the wait, and locked again after the wait.
Typical use:
Thread A:
SDL_LockMutex(lock);
while ( ! condition ) {
SDL_WaitCondition(cond, lock);
}
SDL_UnlockMutex(lock);
Thread B:
SDL_LockMutex(lock);
...
condition = true;
...
SDL_SignalCondition(cond);
SDL_UnlockMutex(lock);
*/
extern "C" int
SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
if (mutex == NULL) {
return SDL_InvalidParamError("mutex");
}
try {
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->cpp_mutex, std::adopt_lock_t());
if (timeoutNS < 0) {
cond->cpp_cond.wait(
cpp_lock);
cpp_lock.release();
return 0;
} else {
auto wait_result = cond->cpp_cond.wait_for(
cpp_lock,
std::chrono::duration<Sint64, std::nano>(timeoutNS));
cpp_lock.release();
if (wait_result == std::cv_status::timeout) {
return SDL_MUTEX_TIMEDOUT;
} else {
return 0;
}
}
} catch (std::system_error &ex) {
return SDL_SetError("Unable to wait on a C++ condition variable: code=%d; %s", ex.code(), ex.what());
}
}

View File

@ -0,0 +1,100 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
extern "C" {
#include "SDL_systhread_c.h"
}
#include <system_error>
#include "SDL_sysmutex_c.h"
#include <Windows.h>
/* Create a mutex */
extern "C" SDL_Mutex *
SDL_CreateMutex(void)
{
/* Allocate and initialize the mutex */
try {
SDL_Mutex *mutex = new SDL_Mutex;
return mutex;
} catch (std::system_error &ex) {
SDL_SetError("unable to create a C++ mutex: code=%d; %s", ex.code(), ex.what());
return NULL;
} catch (std::bad_alloc &) {
SDL_OutOfMemory();
return NULL;
}
}
/* Free the mutex */
extern "C" void
SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex != NULL) {
delete mutex;
}
}
/* Lock the mutex */
extern "C" int
SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (mutex == NULL) {
return 0;
}
try {
mutex->cpp_mutex.lock();
return 0;
} catch (std::system_error &ex) {
return SDL_SetError("unable to lock a C++ mutex: code=%d; %s", ex.code(), ex.what());
}
}
/* TryLock the mutex */
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
int retval = 0;
if (mutex == NULL) {
return 0;
}
if (mutex->cpp_mutex.try_lock() == false) {
retval = SDL_MUTEX_TIMEDOUT;
}
return retval;
}
/* Unlock the mutex */
extern "C" int
SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (mutex == NULL) {
return 0;
}
mutex->cpp_mutex.unlock();
return 0;
}

View File

@ -0,0 +1,29 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <mutex>
struct SDL_Mutex
{
std::recursive_mutex cpp_mutex;
};

View File

@ -0,0 +1,130 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include <shared_mutex>
#include <system_error>
#include <Windows.h>
struct SDL_RWLock
{
std::shared_mutex cpp_mutex;
SDL_threadID write_owner;
};
/* Create a rwlock */
extern "C" SDL_RWLock *SDL_CreateRWLock(void)
{
/* Allocate and initialize the rwlock */
try {
SDL_RWLock *rwlock = new SDL_RWLock;
return rwlock;
} catch (std::system_error &ex) {
SDL_SetError("unable to create a C++ rwlock: code=%d; %s", ex.code(), ex.what());
return NULL;
} catch (std::bad_alloc &) {
SDL_OutOfMemory();
return NULL;
}
}
/* Free the rwlock */
extern "C" void SDL_DestroyRWLock(SDL_RWLock *rwlock)
{
if (rwlock != NULL) {
delete rwlock;
}
}
/* Lock the rwlock */
extern "C" int SDL_LockRWLockForReading(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
}
try {
rwlock->cpp_mutex.lock_shared();
return 0;
} catch (std::system_error &ex) {
return SDL_SetError("unable to lock a C++ rwlock: code=%d; %s", ex.code(), ex.what());
}
}
/* Lock the rwlock for writing */
extern "C" int SDL_LockRWLockForWriting(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
}
try {
rwlock->cpp_mutex.lock();
rwlock->write_owner = SDL_ThreadID();
return 0;
} catch (std::system_error &ex) {
return SDL_SetError("unable to lock a C++ rwlock: code=%d; %s", ex.code(), ex.what());
}
}
/* TryLock the rwlock for reading */
int SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
{
int retval = 0;
if (!rwlock) {
retval = SDL_InvalidParamError("rwlock");
} else if (rwlock->cpp_mutex.try_lock_shared() == false) {
retval = SDL_RWLOCK_TIMEDOUT;
}
return retval;
}
/* TryLock the rwlock for writing */
int SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
{
int retval = 0;
if (!rwlock) {
retval = SDL_InvalidParamError("rwlock");
} else if (rwlock->cpp_mutex.try_lock() == false) {
retval = SDL_RWLOCK_TIMEDOUT;
} else {
rwlock->write_owner = SDL_ThreadID();
}
return retval;
}
/* Unlock the rwlock */
extern "C" int
SDL_UnlockRWLock(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
if (!rwlock) {
return SDL_InvalidParamError("rwlock");
} else if (rwlock->write_owner == SDL_ThreadID()) {
rwlock->write_owner = 0;
rwlock->cpp_mutex.unlock();
} else {
rwlock->cpp_mutex.unlock_shared();
}
return 0;
}

View File

@ -0,0 +1,161 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* Thread management routines for SDL */
extern "C" {
#include "../SDL_thread_c.h"
#include "../SDL_systhread.h"
}
#include <mutex>
#include <thread>
#include <system_error>
#ifdef __WINRT__
#include <Windows.h>
#endif
static void RunThread(void *args)
{
SDL_RunThread((SDL_Thread *)args);
}
extern "C" int
SDL_SYS_CreateThread(SDL_Thread *thread)
{
try {
// !!! FIXME: no way to set a thread stack size here.
std::thread cpp_thread(RunThread, thread);
thread->handle = (void *)new std::thread(std::move(cpp_thread));
return 0;
} catch (std::system_error &ex) {
return SDL_SetError("unable to start a C++ thread: code=%d; %s", ex.code(), ex.what());
} catch (std::bad_alloc &) {
return SDL_OutOfMemory();
}
}
extern "C" void
SDL_SYS_SetupThread(const char *name)
{
// Make sure a thread ID gets assigned ASAP, for debugging purposes:
SDL_ThreadID();
return;
}
extern "C" SDL_threadID
SDL_ThreadID(void)
{
#ifdef __WINRT__
return GetCurrentThreadId();
#else
// HACK: Mimic a thread ID, if one isn't otherwise available.
static thread_local SDL_threadID current_thread_id = 0;
static SDL_threadID next_thread_id = 1;
static std::mutex next_thread_id_mutex;
if (current_thread_id == 0) {
std::lock_guard<std::mutex> lock(next_thread_id_mutex);
current_thread_id = next_thread_id;
++next_thread_id;
}
return current_thread_id;
#endif
}
extern "C" int
SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
#ifdef __WINRT__
int value;
if (priority == SDL_THREAD_PRIORITY_LOW) {
value = THREAD_PRIORITY_LOWEST;
} else if (priority == SDL_THREAD_PRIORITY_HIGH) {
value = THREAD_PRIORITY_HIGHEST;
} else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
// FIXME: WinRT does not support TIME_CRITICAL! -flibit
SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "TIME_CRITICAL unsupported, falling back to HIGHEST");
value = THREAD_PRIORITY_HIGHEST;
} else {
value = THREAD_PRIORITY_NORMAL;
}
if (!SetThreadPriority(GetCurrentThread(), value)) {
return WIN_SetError("SetThreadPriority()");
}
return 0;
#else
return SDL_Unsupported();
#endif
}
extern "C" void
SDL_SYS_WaitThread(SDL_Thread *thread)
{
if (!thread) {
return;
}
try {
std::thread *cpp_thread = (std::thread *)thread->handle;
if (cpp_thread->joinable()) {
cpp_thread->join();
}
} catch (std::system_error &) {
// An error occurred when joining the thread. SDL_WaitThread does not,
// however, seem to provide a means to report errors to its callers
// though!
}
}
extern "C" void
SDL_SYS_DetachThread(SDL_Thread *thread)
{
if (!thread) {
return;
}
try {
std::thread *cpp_thread = (std::thread *)thread->handle;
if (cpp_thread->joinable()) {
cpp_thread->detach();
}
} catch (std::system_error &) {
// An error occurred when detaching the thread. SDL_DetachThread does not,
// however, seem to provide a means to report errors to its callers
// though!
}
}
extern "C" SDL_TLSData *
SDL_SYS_GetTLSData(void)
{
return SDL_Generic_GetTLSData();
}
extern "C" int
SDL_SYS_SetTLSData(SDL_TLSData *data)
{
return SDL_Generic_SetTLSData(data);
}

View File

@ -0,0 +1,24 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/* For a thread handle, use a void pointer to a std::thread */
typedef void *SYS_ThreadHandle;

View File

@ -0,0 +1,204 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_VITA
/* An implementation of condition variables using semaphores and mutexes */
/*
This implementation borrows heavily from the BeOS condition variable
implementation, written by Christopher Tate and Owen Smith. Thanks!
*/
struct SDL_Condition
{
SDL_Mutex *lock;
int waiting;
int signals;
SDL_Semaphore *wait_sem;
SDL_Semaphore *wait_done;
};
/* Create a condition variable */
SDL_Condition *SDL_CreateCondition(void)
{
SDL_Condition *cond;
cond = (SDL_Condition *)SDL_malloc(sizeof(SDL_Condition));
if (cond != NULL) {
cond->lock = SDL_CreateMutex();
cond->wait_sem = SDL_CreateSemaphore(0);
cond->wait_done = SDL_CreateSemaphore(0);
cond->waiting = cond->signals = 0;
if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
SDL_DestroyCondition(cond);
cond = NULL;
}
} else {
SDL_OutOfMemory();
}
return cond;
}
/* Destroy a condition variable */
void SDL_DestroyCondition(SDL_Condition *cond)
{
if (cond != NULL) {
if (cond->wait_sem) {
SDL_DestroySemaphore(cond->wait_sem);
}
if (cond->wait_done) {
SDL_DestroySemaphore(cond->wait_done);
}
if (cond->lock) {
SDL_DestroyMutex(cond->lock);
}
SDL_free(cond);
}
}
/* Restart one of the threads that are waiting on the condition variable */
int SDL_SignalCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
++cond->signals;
SDL_PostSemaphore(cond->wait_sem);
SDL_UnlockMutex(cond->lock);
SDL_WaitSemaphore(cond->wait_done);
} else {
SDL_UnlockMutex(cond->lock);
}
return 0;
}
/* Restart all threads that are waiting on the condition variable */
int SDL_BroadcastCondition(SDL_Condition *cond)
{
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* If there are waiting threads not already signalled, then
signal the condition and wait for the thread to respond.
*/
SDL_LockMutex(cond->lock);
if (cond->waiting > cond->signals) {
int i, num_waiting;
num_waiting = (cond->waiting - cond->signals);
cond->signals = cond->waiting;
for (i = 0; i < num_waiting; ++i) {
SDL_PostSemaphore(cond->wait_sem);
}
/* Now all released threads are blocked here, waiting for us.
Collect them all (and win fabulous prizes!) :-)
*/
SDL_UnlockMutex(cond->lock);
for (i = 0; i < num_waiting; ++i) {
SDL_WaitSemaphore(cond->wait_done);
}
} else {
SDL_UnlockMutex(cond->lock);
}
return 0;
}
/* Wait on the condition variable for at most 'timeoutNS' nanoseconds.
The mutex must be locked before entering this function!
The mutex is unlocked during the wait, and locked again after the wait.
Typical use:
Thread A:
SDL_LockMutex(lock);
while ( ! condition ) {
SDL_WaitCondition(cond, lock);
}
SDL_UnlockMutex(lock);
Thread B:
SDL_LockMutex(lock);
...
condition = true;
...
SDL_SignalCondition(cond);
SDL_UnlockMutex(lock);
*/
int SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
int retval;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
/* Obtain the protection mutex, and increment the number of waiters.
This allows the signal mechanism to only perform a signal if there
are waiting threads.
*/
SDL_LockMutex(cond->lock);
++cond->waiting;
SDL_UnlockMutex(cond->lock);
/* Unlock the mutex, as is required by condition variable semantics */
SDL_UnlockMutex(mutex);
/* Wait for a signal */
retval = SDL_WaitSemaphoreTimeoutNS(cond->wait_sem, timeoutNS);
/* Let the signaler know we have completed the wait, otherwise
the signaler can race ahead and get the condition semaphore
if we are stopped between the mutex unlock and semaphore wait,
giving a deadlock. See the following URL for details:
http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
*/
SDL_LockMutex(cond->lock);
if (cond->signals > 0) {
/* If we timed out, we need to eat a condition signal */
if (retval > 0) {
SDL_WaitSemaphore(cond->wait_sem);
}
/* We always notify the signal thread that we are done */
SDL_PostSemaphore(cond->wait_done);
/* Signal handshake complete */
--cond->signals;
}
--cond->waiting;
SDL_UnlockMutex(cond->lock);
/* Lock the mutex, as is required by condition variable semantics */
SDL_LockMutex(mutex);
return retval;
}
#endif /* SDL_THREAD_VITA */

View File

@ -0,0 +1,141 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_VITA
#include "SDL_systhread_c.h"
#include <psp2/kernel/threadmgr.h>
#include <psp2/kernel/error.h>
struct SDL_Mutex
{
SceKernelLwMutexWork lock;
};
/* Create a mutex */
SDL_Mutex *SDL_CreateMutex(void)
{
SDL_Mutex *mutex = NULL;
SceInt32 res = 0;
/* Allocate mutex memory */
mutex = (SDL_Mutex *)SDL_malloc(sizeof(*mutex));
if (mutex != NULL) {
res = sceKernelCreateLwMutex(
&mutex->lock,
"SDL mutex",
SCE_KERNEL_MUTEX_ATTR_RECURSIVE,
0,
NULL);
if (res < 0) {
SDL_SetError("Error trying to create mutex: %x", res);
}
} else {
SDL_OutOfMemory();
}
return mutex;
}
/* Free the mutex */
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex != NULL) {
sceKernelDeleteLwMutex(&mutex->lock);
SDL_free(mutex);
}
}
/* Lock the mutex */
int SDL_LockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
SceInt32 res = 0;
if (mutex == NULL) {
return 0;
}
res = sceKernelLockLwMutex(&mutex->lock, 1, NULL);
if (res != SCE_KERNEL_OK) {
return SDL_SetError("Error trying to lock mutex: %x", res);
}
return 0;
#endif /* SDL_THREADS_DISABLED */
}
/* Try to lock the mutex */
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
SceInt32 res = 0;
if (mutex == NULL) {
return 0;
}
res = sceKernelTryLockLwMutex(&mutex->lock, 1);
switch (res) {
case SCE_KERNEL_OK:
return 0;
break;
case SCE_KERNEL_ERROR_MUTEX_FAILED_TO_OWN:
return SDL_MUTEX_TIMEDOUT;
break;
default:
return SDL_SetError("Error trying to lock mutex: %x", res);
break;
}
return -1;
#endif /* SDL_THREADS_DISABLED */
}
/* Unlock the mutex */
int SDL_UnlockMutex(SDL_Mutex *mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
#ifdef SDL_THREADS_DISABLED
return 0;
#else
SceInt32 res = 0;
if (mutex == NULL) {
return 0;
}
res = sceKernelUnlockLwMutex(&mutex->lock, 1);
if (res != 0) {
return SDL_SetError("Error trying to unlock mutex: %x", res);
}
return 0;
#endif /* SDL_THREADS_DISABLED */
}
#endif /* SDL_THREAD_VITA */

View File

@ -0,0 +1,21 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"

View File

@ -0,0 +1,147 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_VITA
/* Semaphore functions for the VITA. */
#include <stdio.h>
#include <stdlib.h>
#include <psp2/types.h>
#include <psp2/kernel/error.h>
#include <psp2/kernel/threadmgr.h>
struct SDL_Semaphore
{
SceUID semid;
};
/* Create a semaphore */
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
SDL_Semaphore *sem;
sem = (SDL_Semaphore *)SDL_malloc(sizeof(*sem));
if (sem != NULL) {
/* TODO: Figure out the limit on the maximum value. */
sem->semid = sceKernelCreateSema("SDL sema", 0, initial_value, 255, NULL);
if (sem->semid < 0) {
SDL_SetError("Couldn't create semaphore");
SDL_free(sem);
sem = NULL;
}
} else {
SDL_OutOfMemory();
}
return sem;
}
/* Free the semaphore */
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
if (sem != NULL) {
if (sem->semid > 0) {
sceKernelDeleteSema(sem->semid);
sem->semid = 0;
}
SDL_free(sem);
}
}
/* TODO: This routine is a bit overloaded.
* If the timeout is 0 then just poll the semaphore; if it's SDL_MUTEX_MAXWAIT, pass
* NULL to sceKernelWaitSema() so that it waits indefinitely; and if the timeout
* is specified, convert it to microseconds. */
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
SceUInt timeoutUS;
SceUInt *pTimeout;
int res;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeoutNS == 0) {
res = sceKernelPollSema(sem->semid, 1);
if (res < 0) {
return SDL_MUTEX_TIMEDOUT;
}
return 0;
}
if (timeoutNS < 0) {
pTimeout = NULL;
} else {
timeoutUS = (SceUInt)SDL_NS_TO_US(timeoutNS); /* Convert to microseconds. */
pTimeout = &timeoutUS;
}
res = sceKernelWaitSema(sem->semid, 1, pTimeout);
switch (res) {
case SCE_KERNEL_OK:
return 0;
case SCE_KERNEL_ERROR_WAIT_TIMEOUT:
return SDL_MUTEX_TIMEDOUT;
default:
return SDL_SetError("sceKernelWaitSema() failed");
}
}
/* Returns the current count of the semaphore */
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
SceKernelSemaInfo info;
info.size = sizeof(info);
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
if (sceKernelGetSemaInfo(sem->semid, &info) >= 0) {
return info.currentCount;
}
return 0;
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
int res;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
res = sceKernelSignalSema(sem->semid, 1);
if (res < 0) {
return SDL_SetError("sceKernelSignalSema() failed");
}
return 0;
}
#endif /* SDL_THREAD_VITA */

View File

@ -0,0 +1,133 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_VITA
/* VITA thread management routines for SDL */
#include <stdio.h>
#include <stdlib.h>
#include "../SDL_systhread.h"
#include "../SDL_thread_c.h"
#include <psp2/types.h>
#include <psp2/kernel/threadmgr.h>
#define VITA_THREAD_STACK_SIZE_MIN 0x1000 // 4KiB
#define VITA_THREAD_STACK_SIZE_MAX 0x2000000 // 32MiB
#define VITA_THREAD_STACK_SIZE_DEFAULT 0x10000 // 64KiB
#define VITA_THREAD_NAME_MAX 32
#define VITA_THREAD_PRIORITY_LOW 191
#define VITA_THREAD_PRIORITY_NORMAL 160
#define VITA_THREAD_PRIORITY_HIGH 112
#define VITA_THREAD_PRIORITY_TIME_CRITICAL 64
static int ThreadEntry(SceSize args, void *argp)
{
SDL_RunThread(*(SDL_Thread **)argp);
return 0;
}
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
char thread_name[VITA_THREAD_NAME_MAX];
size_t stack_size = VITA_THREAD_STACK_SIZE_DEFAULT;
SDL_strlcpy(thread_name, "SDL thread", VITA_THREAD_NAME_MAX);
if (thread->name) {
SDL_strlcpy(thread_name, thread->name, VITA_THREAD_NAME_MAX);
}
if (thread->stacksize) {
if (thread->stacksize < VITA_THREAD_STACK_SIZE_MIN) {
thread->stacksize = VITA_THREAD_STACK_SIZE_MIN;
}
if (thread->stacksize > VITA_THREAD_STACK_SIZE_MAX) {
thread->stacksize = VITA_THREAD_STACK_SIZE_MAX;
}
stack_size = thread->stacksize;
}
/* Create new thread with the same priority as the current thread */
thread->handle = sceKernelCreateThread(
thread_name, // name
ThreadEntry, // function to run
0, // priority. 0 means priority of calling thread
stack_size, // stack size
0, // attributes. always 0
0, // cpu affinity mask. 0 = all CPUs
NULL // opt. always NULL
);
if (thread->handle < 0) {
return SDL_SetError("sceKernelCreateThread() failed");
}
sceKernelStartThread(thread->handle, 4, &thread);
return 0;
}
void SDL_SYS_SetupThread(const char *name)
{
/* Do nothing. */
}
SDL_threadID SDL_ThreadID(void)
{
return (SDL_threadID)sceKernelGetThreadId();
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
sceKernelWaitThreadEnd(thread->handle, NULL, NULL);
sceKernelDeleteThread(thread->handle);
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
/* Do nothing. */
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
int value = VITA_THREAD_PRIORITY_NORMAL;
switch (priority) {
case SDL_THREAD_PRIORITY_LOW:
value = VITA_THREAD_PRIORITY_LOW;
break;
case SDL_THREAD_PRIORITY_NORMAL:
value = VITA_THREAD_PRIORITY_NORMAL;
break;
case SDL_THREAD_PRIORITY_HIGH:
value = VITA_THREAD_PRIORITY_HIGH;
break;
case SDL_THREAD_PRIORITY_TIME_CRITICAL:
value = VITA_THREAD_PRIORITY_TIME_CRITICAL;
break;
}
return sceKernelChangeThreadPriority(0, value);
}
#endif /* SDL_THREAD_VITA */

View File

@ -0,0 +1,24 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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 <psp2/types.h>
typedef SceUID SYS_ThreadHandle;

View File

@ -0,0 +1,269 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include "../generic/SDL_syscond_c.h"
#include "SDL_sysmutex_c.h"
typedef SDL_Condition *(*pfnSDL_CreateCondition)(void);
typedef void (*pfnSDL_DestroyCondition)(SDL_Condition *);
typedef int (*pfnSDL_SignalCondition)(SDL_Condition *);
typedef int (*pfnSDL_BroadcastCondition)(SDL_Condition *);
typedef int (*pfnSDL_WaitConditionTimeoutNS)(SDL_Condition *, SDL_Mutex *, Sint64);
typedef struct SDL_cond_impl_t
{
pfnSDL_CreateCondition Create;
pfnSDL_DestroyCondition Destroy;
pfnSDL_SignalCondition Signal;
pfnSDL_BroadcastCondition Broadcast;
pfnSDL_WaitConditionTimeoutNS WaitTimeoutNS;
} SDL_cond_impl_t;
/* Implementation will be chosen at runtime based on available Kernel features */
static SDL_cond_impl_t SDL_cond_impl_active = { 0 };
/**
* Native Windows Condition Variable (SRW Locks)
*/
#ifndef CONDITION_VARIABLE_INIT
#define CONDITION_VARIABLE_INIT \
{ \
0 \
}
typedef struct CONDITION_VARIABLE
{
PVOID Ptr;
} CONDITION_VARIABLE, *PCONDITION_VARIABLE;
#endif
#ifdef __WINRT__
#define pWakeConditionVariable WakeConditionVariable
#define pWakeAllConditionVariable WakeAllConditionVariable
#define pSleepConditionVariableSRW SleepConditionVariableSRW
#define pSleepConditionVariableCS SleepConditionVariableCS
#else
typedef VOID(WINAPI *pfnWakeConditionVariable)(PCONDITION_VARIABLE);
typedef VOID(WINAPI *pfnWakeAllConditionVariable)(PCONDITION_VARIABLE);
typedef BOOL(WINAPI *pfnSleepConditionVariableSRW)(PCONDITION_VARIABLE, PSRWLOCK, DWORD, ULONG);
typedef BOOL(WINAPI *pfnSleepConditionVariableCS)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD);
static pfnWakeConditionVariable pWakeConditionVariable = NULL;
static pfnWakeAllConditionVariable pWakeAllConditionVariable = NULL;
static pfnSleepConditionVariableSRW pSleepConditionVariableSRW = NULL;
static pfnSleepConditionVariableCS pSleepConditionVariableCS = NULL;
#endif
typedef struct SDL_cond_cv
{
CONDITION_VARIABLE cond;
} SDL_cond_cv;
static SDL_Condition *SDL_CreateCondition_cv(void)
{
SDL_cond_cv *cond;
/* Relies on CONDITION_VARIABLE_INIT == 0. */
cond = (SDL_cond_cv *)SDL_calloc(1, sizeof(*cond));
if (cond == NULL) {
SDL_OutOfMemory();
}
return (SDL_Condition *)cond;
}
static void SDL_DestroyCondition_cv(SDL_Condition *cond)
{
if (cond != NULL) {
/* There are no kernel allocated resources */
SDL_free(cond);
}
}
static int SDL_SignalCondition_cv(SDL_Condition *_cond)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
pWakeConditionVariable(&cond->cond);
return 0;
}
static int SDL_BroadcastCondition_cv(SDL_Condition *_cond)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
pWakeAllConditionVariable(&cond->cond);
return 0;
}
static int SDL_WaitConditionTimeoutNS_cv(SDL_Condition *_cond, SDL_Mutex *_mutex, Sint64 timeoutNS)
{
SDL_cond_cv *cond = (SDL_cond_cv *)_cond;
DWORD timeout;
int ret;
if (cond == NULL) {
return SDL_InvalidParamError("cond");
}
if (_mutex == NULL) {
return SDL_InvalidParamError("mutex");
}
if (timeoutNS < 0) {
timeout = INFINITE;
} else {
timeout = (DWORD)SDL_NS_TO_MS(timeoutNS);
}
if (SDL_mutex_impl_active.Type == SDL_MUTEX_SRW) {
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
if (mutex->count != 1 || mutex->owner != GetCurrentThreadId()) {
return SDL_SetError("Passed mutex is not locked or locked recursively");
}
/* The mutex must be updated to the released state */
mutex->count = 0;
mutex->owner = 0;
if (pSleepConditionVariableSRW(&cond->cond, &mutex->srw, timeout, 0) == FALSE) {
if (GetLastError() == ERROR_TIMEOUT) {
ret = SDL_MUTEX_TIMEDOUT;
} else {
ret = SDL_SetError("SleepConditionVariableSRW() failed");
}
} else {
ret = 0;
}
/* The mutex is owned by us again, regardless of status of the wait */
SDL_assert(mutex->count == 0 && mutex->owner == 0);
mutex->count = 1;
mutex->owner = GetCurrentThreadId();
} else {
SDL_mutex_cs *mutex = (SDL_mutex_cs *)_mutex;
SDL_assert(SDL_mutex_impl_active.Type == SDL_MUTEX_CS);
if (pSleepConditionVariableCS(&cond->cond, &mutex->cs, timeout) == FALSE) {
if (GetLastError() == ERROR_TIMEOUT) {
ret = SDL_MUTEX_TIMEDOUT;
} else {
ret = SDL_SetError("SleepConditionVariableCS() failed");
}
} else {
ret = 0;
}
}
return ret;
}
static const SDL_cond_impl_t SDL_cond_impl_cv = {
&SDL_CreateCondition_cv,
&SDL_DestroyCondition_cv,
&SDL_SignalCondition_cv,
&SDL_BroadcastCondition_cv,
&SDL_WaitConditionTimeoutNS_cv,
};
#ifndef __WINRT__
/* Generic Condition Variable implementation using SDL_Mutex and SDL_Semaphore */
static const SDL_cond_impl_t SDL_cond_impl_generic = {
&SDL_CreateCondition_generic,
&SDL_DestroyCondition_generic,
&SDL_SignalCondition_generic,
&SDL_BroadcastCondition_generic,
&SDL_WaitConditionTimeoutNS_generic,
};
#endif
SDL_Condition *SDL_CreateCondition(void)
{
if (SDL_cond_impl_active.Create == NULL) {
const SDL_cond_impl_t *impl = NULL;
if (SDL_mutex_impl_active.Type == SDL_MUTEX_INVALID) {
/* The mutex implementation isn't decided yet, trigger it */
SDL_Mutex *mutex = SDL_CreateMutex();
if (mutex == NULL) {
return NULL;
}
SDL_DestroyMutex(mutex);
SDL_assert(SDL_mutex_impl_active.Type != SDL_MUTEX_INVALID);
}
#ifdef __WINRT__
/* Link statically on this platform */
impl = &SDL_cond_impl_cv;
#else
/* Default to generic implementation, works with all mutex implementations */
impl = &SDL_cond_impl_generic;
{
HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
pWakeConditionVariable = (pfnWakeConditionVariable)GetProcAddress(kernel32, "WakeConditionVariable");
pWakeAllConditionVariable = (pfnWakeAllConditionVariable)GetProcAddress(kernel32, "WakeAllConditionVariable");
pSleepConditionVariableSRW = (pfnSleepConditionVariableSRW)GetProcAddress(kernel32, "SleepConditionVariableSRW");
pSleepConditionVariableCS = (pfnSleepConditionVariableCS)GetProcAddress(kernel32, "SleepConditionVariableCS");
if (pWakeConditionVariable && pWakeAllConditionVariable && pSleepConditionVariableSRW && pSleepConditionVariableCS) {
/* Use the Windows provided API */
impl = &SDL_cond_impl_cv;
}
}
}
#endif
SDL_copyp(&SDL_cond_impl_active, impl);
}
return SDL_cond_impl_active.Create();
}
void SDL_DestroyCondition(SDL_Condition *cond)
{
SDL_cond_impl_active.Destroy(cond);
}
int SDL_SignalCondition(SDL_Condition *cond)
{
return SDL_cond_impl_active.Signal(cond);
}
int SDL_BroadcastCondition(SDL_Condition *cond)
{
return SDL_cond_impl_active.Broadcast(cond);
}
int SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS)
{
return SDL_cond_impl_active.WaitTimeoutNS(cond, mutex, timeoutNS);
}

View File

@ -0,0 +1,290 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_WINDOWS
/**
* Mutex functions using the Win32 API
* There are two implementations available based on:
* - Critical Sections. Available on all OS versions since Windows XP.
* - Slim Reader/Writer Locks. Requires Windows 7 or newer.
* which are chosen at runtime.
*/
#include "SDL_sysmutex_c.h"
/* Implementation will be chosen at runtime based on available Kernel features */
SDL_mutex_impl_t SDL_mutex_impl_active = { 0 };
/**
* Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer.
*/
#ifdef __WINRT__
/* Functions are guaranteed to be available */
#define pInitializeSRWLock InitializeSRWLock
#define pReleaseSRWLockExclusive ReleaseSRWLockExclusive
#define pAcquireSRWLockExclusive AcquireSRWLockExclusive
#define pTryAcquireSRWLockExclusive TryAcquireSRWLockExclusive
#else
typedef VOID(WINAPI *pfnInitializeSRWLock)(PSRWLOCK);
typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK);
typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK);
typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK);
static pfnInitializeSRWLock pInitializeSRWLock = NULL;
static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL;
static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL;
static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL;
#endif
static SDL_Mutex *SDL_CreateMutex_srw(void)
{
SDL_mutex_srw *mutex;
mutex = (SDL_mutex_srw *)SDL_calloc(1, sizeof(*mutex));
if (mutex == NULL) {
SDL_OutOfMemory();
}
pInitializeSRWLock(&mutex->srw);
return (SDL_Mutex *)mutex;
}
static void SDL_DestroyMutex_srw(SDL_Mutex *mutex)
{
/* There are no kernel allocated resources */
SDL_free(mutex);
}
static int SDL_LockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
DWORD this_thread;
this_thread = GetCurrentThreadId();
if (mutex->owner == this_thread) {
++mutex->count;
} else {
/* The order of operations is important.
We set the locking thread id after we obtain the lock
so unlocks from other threads will fail.
*/
pAcquireSRWLockExclusive(&mutex->srw);
SDL_assert(mutex->count == 0 && mutex->owner == 0);
mutex->owner = this_thread;
mutex->count = 1;
}
return 0;
}
static int SDL_TryLockMutex_srw(SDL_Mutex *_mutex)
{
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
DWORD this_thread;
int retval = 0;
this_thread = GetCurrentThreadId();
if (mutex->owner == this_thread) {
++mutex->count;
} else {
if (pTryAcquireSRWLockExclusive(&mutex->srw) != 0) {
SDL_assert(mutex->count == 0 && mutex->owner == 0);
mutex->owner = this_thread;
mutex->count = 1;
} else {
retval = SDL_MUTEX_TIMEDOUT;
}
}
return retval;
}
static int SDL_UnlockMutex_srw(SDL_Mutex *_mutex) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
SDL_mutex_srw *mutex = (SDL_mutex_srw *)_mutex;
if (mutex->owner == GetCurrentThreadId()) {
if (--mutex->count == 0) {
mutex->owner = 0;
pReleaseSRWLockExclusive(&mutex->srw);
}
} else {
return SDL_SetError("mutex not owned by this thread");
}
return 0;
}
static const SDL_mutex_impl_t SDL_mutex_impl_srw = {
&SDL_CreateMutex_srw,
&SDL_DestroyMutex_srw,
&SDL_LockMutex_srw,
&SDL_TryLockMutex_srw,
&SDL_UnlockMutex_srw,
SDL_MUTEX_SRW,
};
/**
* Fallback Mutex implementation using Critical Sections (before Win 7)
*/
/* Create a mutex */
static SDL_Mutex *SDL_CreateMutex_cs(void)
{
SDL_mutex_cs *mutex;
/* Allocate mutex memory */
mutex = (SDL_mutex_cs *)SDL_malloc(sizeof(*mutex));
if (mutex != NULL) {
/* Initialize */
/* On SMP systems, a non-zero spin count generally helps performance */
#ifdef __WINRT__
InitializeCriticalSectionEx(&mutex->cs, 2000, 0);
#else
InitializeCriticalSectionAndSpinCount(&mutex->cs, 2000);
#endif
} else {
SDL_OutOfMemory();
}
return (SDL_Mutex *)mutex;
}
/* Free the mutex */
static void SDL_DestroyMutex_cs(SDL_Mutex *mutex_)
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
DeleteCriticalSection(&mutex->cs);
SDL_free(mutex);
}
/* Lock the mutex */
static int SDL_LockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
EnterCriticalSection(&mutex->cs);
return 0;
}
/* TryLock the mutex */
static int SDL_TryLockMutex_cs(SDL_Mutex *mutex_)
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
int retval = 0;
if (TryEnterCriticalSection(&mutex->cs) == 0) {
retval = SDL_MUTEX_TIMEDOUT;
}
return retval;
}
/* Unlock the mutex */
static int SDL_UnlockMutex_cs(SDL_Mutex *mutex_) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
SDL_mutex_cs *mutex = (SDL_mutex_cs *)mutex_;
LeaveCriticalSection(&mutex->cs);
return 0;
}
static const SDL_mutex_impl_t SDL_mutex_impl_cs = {
&SDL_CreateMutex_cs,
&SDL_DestroyMutex_cs,
&SDL_LockMutex_cs,
&SDL_TryLockMutex_cs,
&SDL_UnlockMutex_cs,
SDL_MUTEX_CS,
};
/**
* Runtime selection and redirection
*/
SDL_Mutex *SDL_CreateMutex(void)
{
if (SDL_mutex_impl_active.Create == NULL) {
/* Default to fallback implementation */
const SDL_mutex_impl_t *impl = &SDL_mutex_impl_cs;
if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS, SDL_FALSE)) {
#ifdef __WINRT__
/* Link statically on this platform */
impl = &SDL_mutex_impl_srw;
#else
/* Try faster implementation for Windows 7 and newer */
HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
/* Requires Vista: */
pInitializeSRWLock = (pfnInitializeSRWLock)GetProcAddress(kernel32, "InitializeSRWLock");
pReleaseSRWLockExclusive = (pfnReleaseSRWLockExclusive)GetProcAddress(kernel32, "ReleaseSRWLockExclusive");
pAcquireSRWLockExclusive = (pfnAcquireSRWLockExclusive)GetProcAddress(kernel32, "AcquireSRWLockExclusive");
/* Requires 7: */
pTryAcquireSRWLockExclusive = (pfnTryAcquireSRWLockExclusive)GetProcAddress(kernel32, "TryAcquireSRWLockExclusive");
if (pInitializeSRWLock && pReleaseSRWLockExclusive && pAcquireSRWLockExclusive && pTryAcquireSRWLockExclusive) {
impl = &SDL_mutex_impl_srw;
}
}
#endif
}
/* Copy instead of using pointer to save one level of indirection */
SDL_copyp(&SDL_mutex_impl_active, impl);
}
return SDL_mutex_impl_active.Create();
}
void SDL_DestroyMutex(SDL_Mutex *mutex)
{
if (mutex) {
SDL_mutex_impl_active.Destroy(mutex);
}
}
int SDL_LockMutex(SDL_Mutex *mutex)
{
if (mutex == NULL) {
return 0;
}
return SDL_mutex_impl_active.Lock(mutex);
}
int SDL_TryLockMutex(SDL_Mutex *mutex)
{
if (mutex == NULL) {
return 0;
}
return SDL_mutex_impl_active.TryLock(mutex);
}
int SDL_UnlockMutex(SDL_Mutex *mutex)
{
if (mutex == NULL) {
return 0;
}
return SDL_mutex_impl_active.Unlock(mutex);
}
#endif /* SDL_THREAD_WINDOWS */

View File

@ -0,0 +1,73 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#include "../../core/windows/SDL_windows.h"
typedef SDL_Mutex *(*pfnSDL_CreateMutex)(void);
typedef int (*pfnSDL_LockMutex)(SDL_Mutex *);
typedef int (*pfnSDL_TryLockMutex)(SDL_Mutex *);
typedef int (*pfnSDL_UnlockMutex)(SDL_Mutex *);
typedef void (*pfnSDL_DestroyMutex)(SDL_Mutex *);
typedef enum
{
SDL_MUTEX_INVALID = 0,
SDL_MUTEX_SRW,
SDL_MUTEX_CS,
} SDL_MutexType;
typedef struct SDL_mutex_impl_t
{
pfnSDL_CreateMutex Create;
pfnSDL_DestroyMutex Destroy;
pfnSDL_LockMutex Lock;
pfnSDL_TryLockMutex TryLock;
pfnSDL_UnlockMutex Unlock;
/* Needed by SDL_Condition: */
SDL_MutexType Type;
} SDL_mutex_impl_t;
extern SDL_mutex_impl_t SDL_mutex_impl_active;
#ifndef SRWLOCK_INIT
#define SRWLOCK_INIT \
{ \
0 \
}
typedef struct _SRWLOCK
{
PVOID Ptr;
} SRWLOCK, *PSRWLOCK;
#endif
typedef struct SDL_mutex_srw
{
SRWLOCK srw;
/* SRW Locks are not recursive, that has to be handled by SDL: */
DWORD count;
DWORD owner;
} SDL_mutex_srw;
typedef struct SDL_mutex_cs
{
CRITICAL_SECTION cs;
} SDL_mutex_cs;

View File

@ -0,0 +1,258 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
/**
* Implementation based on Slim Reader/Writer (SRW) Locks for Win 7 and newer.
*/
/* This header makes sure SRWLOCK is actually declared, even on ancient WinSDKs. */
#include "SDL_sysmutex_c.h"
typedef VOID(WINAPI *pfnInitializeSRWLock)(PSRWLOCK);
typedef VOID(WINAPI *pfnReleaseSRWLockShared)(PSRWLOCK);
typedef VOID(WINAPI *pfnAcquireSRWLockShared)(PSRWLOCK);
typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockShared)(PSRWLOCK);
typedef VOID(WINAPI *pfnReleaseSRWLockExclusive)(PSRWLOCK);
typedef VOID(WINAPI *pfnAcquireSRWLockExclusive)(PSRWLOCK);
typedef BOOLEAN(WINAPI *pfnTryAcquireSRWLockExclusive)(PSRWLOCK);
#ifdef __WINRT__
/* Functions are guaranteed to be available */
#define pTryAcquireSRWLockExclusive TryAcquireSRWLockExclusive
#define pInitializeSRWLock InitializeSRWLock
#define pReleaseSRWLockShared ReleaseSRWLockShared
#define pAcquireSRWLockShared AcquireSRWLockShared
#define pTryAcquireSRWLockShared TryAcquireSRWLockShared
#define pReleaseSRWLockExclusive ReleaseSRWLockExclusive
#define pAcquireSRWLockExclusive AcquireSRWLockExclusive
#define pTryAcquireSRWLockExclusive TryAcquireSRWLockExclusive
#else
static pfnInitializeSRWLock pInitializeSRWLock = NULL;
static pfnReleaseSRWLockShared pReleaseSRWLockShared = NULL;
static pfnAcquireSRWLockShared pAcquireSRWLockShared = NULL;
static pfnTryAcquireSRWLockShared pTryAcquireSRWLockShared = NULL;
static pfnReleaseSRWLockExclusive pReleaseSRWLockExclusive = NULL;
static pfnAcquireSRWLockExclusive pAcquireSRWLockExclusive = NULL;
static pfnTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive = NULL;
#endif
typedef SDL_RWLock *(*pfnSDL_CreateRWLock)(void);
typedef void (*pfnSDL_DestroyRWLock)(SDL_RWLock *);
typedef int (*pfnSDL_LockRWLockForReading)(SDL_RWLock *);
typedef int (*pfnSDL_LockRWLockForWriting)(SDL_RWLock *);
typedef int (*pfnSDL_TryLockRWLockForReading)(SDL_RWLock *);
typedef int (*pfnSDL_TryLockRWLockForWriting)(SDL_RWLock *);
typedef int (*pfnSDL_UnlockRWLock)(SDL_RWLock *);
typedef struct SDL_rwlock_impl_t
{
pfnSDL_CreateRWLock Create;
pfnSDL_DestroyRWLock Destroy;
pfnSDL_LockRWLockForReading LockForReading;
pfnSDL_LockRWLockForWriting LockForWriting;
pfnSDL_TryLockRWLockForReading TryLockForReading;
pfnSDL_TryLockRWLockForWriting TryLockForWriting;
pfnSDL_UnlockRWLock Unlock;
} SDL_rwlock_impl_t;
/* Implementation will be chosen at runtime based on available Kernel features */
static SDL_rwlock_impl_t SDL_rwlock_impl_active = { 0 };
/* rwlock implementation using Win7+ slim read/write locks (SRWLOCK) */
typedef struct SDL_rwlock_srw
{
SRWLOCK srw;
SDL_threadID write_owner;
} SDL_rwlock_srw;
static SDL_RWLock *SDL_CreateRWLock_srw(void)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *)SDL_calloc(1, sizeof(*rwlock));
if (rwlock == NULL) {
SDL_OutOfMemory();
}
pInitializeSRWLock(&rwlock->srw);
return (SDL_RWLock *)rwlock;
}
static void SDL_DestroyRWLock_srw(SDL_RWLock *_rwlock)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
if (rwlock) {
/* There are no kernel allocated resources */
SDL_free(rwlock);
}
}
static int SDL_LockRWLockForReading_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
if (rwlock == NULL) {
return SDL_InvalidParamError("rwlock");
}
pAcquireSRWLockShared(&rwlock->srw);
return 0;
}
static int SDL_LockRWLockForWriting_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
if (rwlock == NULL) {
return SDL_InvalidParamError("rwlock");
}
pAcquireSRWLockExclusive(&rwlock->srw);
rwlock->write_owner = SDL_ThreadID();
return 0;
}
static int SDL_TryLockRWLockForReading_srw(SDL_RWLock *_rwlock)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
int retval = 0;
if (rwlock == NULL) {
retval = SDL_InvalidParamError("rwlock");
} else {
retval = pTryAcquireSRWLockShared(&rwlock->srw) ? 0 : SDL_RWLOCK_TIMEDOUT;
}
return retval;
}
static int SDL_TryLockRWLockForWriting_srw(SDL_RWLock *_rwlock)
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
int retval = 0;
if (rwlock == NULL) {
retval = SDL_InvalidParamError("rwlock");
} else {
retval = pTryAcquireSRWLockExclusive(&rwlock->srw) ? 0 : SDL_RWLOCK_TIMEDOUT;
}
return retval;
}
static int SDL_UnlockRWLock_srw(SDL_RWLock *_rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
SDL_rwlock_srw *rwlock = (SDL_rwlock_srw *) _rwlock;
if (rwlock == NULL) {
return SDL_InvalidParamError("rwlock");
} else if (rwlock->write_owner == SDL_ThreadID()) {
rwlock->write_owner = 0;
pReleaseSRWLockExclusive(&rwlock->srw);
} else {
pReleaseSRWLockShared(&rwlock->srw);
}
return 0;
}
static const SDL_rwlock_impl_t SDL_rwlock_impl_srw = {
&SDL_CreateRWLock_srw,
&SDL_DestroyRWLock_srw,
&SDL_LockRWLockForReading_srw,
&SDL_LockRWLockForWriting_srw,
&SDL_TryLockRWLockForReading_srw,
&SDL_TryLockRWLockForWriting_srw,
&SDL_UnlockRWLock_srw
};
#ifndef __WINRT__
#include "../generic/SDL_sysrwlock_c.h"
/* Generic rwlock implementation using SDL_Mutex, SDL_Condition, and SDL_AtomicInt */
static const SDL_rwlock_impl_t SDL_rwlock_impl_generic = {
&SDL_CreateRWLock_generic,
&SDL_DestroyRWLock_generic,
&SDL_LockRWLockForReading_generic,
&SDL_LockRWLockForWriting_generic,
&SDL_TryLockRWLockForReading_generic,
&SDL_TryLockRWLockForWriting_generic,
&SDL_UnlockRWLock_generic
};
#endif
SDL_RWLock *SDL_CreateRWLock(void)
{
if (SDL_rwlock_impl_active.Create == NULL) {
const SDL_rwlock_impl_t *impl;
#ifdef __WINRT__
/* Link statically on this platform */
impl = &SDL_rwlock_impl_srw;
#else
/* Default to generic implementation, works with all mutex implementations */
impl = &SDL_rwlock_impl_generic;
{
HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
SDL_bool okay = SDL_TRUE;
#define LOOKUP_SRW_SYM(sym) if (okay) { if ((p##sym = (pfn##sym)GetProcAddress(kernel32, #sym)) == NULL) { okay = SDL_FALSE; } }
LOOKUP_SRW_SYM(InitializeSRWLock);
LOOKUP_SRW_SYM(ReleaseSRWLockShared);
LOOKUP_SRW_SYM(AcquireSRWLockShared);
LOOKUP_SRW_SYM(TryAcquireSRWLockShared);
LOOKUP_SRW_SYM(ReleaseSRWLockExclusive);
LOOKUP_SRW_SYM(AcquireSRWLockExclusive);
LOOKUP_SRW_SYM(TryAcquireSRWLockExclusive);
#undef LOOKUP_SRW_SYM
if (okay) {
impl = &SDL_rwlock_impl_srw; /* Use the Windows provided API instead of generic fallback */
}
}
}
#endif
SDL_copyp(&SDL_rwlock_impl_active, impl);
}
return SDL_rwlock_impl_active.Create();
}
void SDL_DestroyRWLock(SDL_RWLock *rwlock)
{
SDL_rwlock_impl_active.Destroy(rwlock);
}
int SDL_LockRWLockForReading(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
return SDL_rwlock_impl_active.LockForReading(rwlock);
}
int SDL_LockRWLockForWriting(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
return SDL_rwlock_impl_active.LockForWriting(rwlock);
}
int SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)
{
return SDL_rwlock_impl_active.TryLockForReading(rwlock);
}
int SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)
{
return SDL_rwlock_impl_active.TryLockForWriting(rwlock);
}
int SDL_UnlockRWLock(SDL_RWLock *rwlock) SDL_NO_THREAD_SAFETY_ANALYSIS /* clang doesn't know about NULL mutexes */
{
return SDL_rwlock_impl_active.Unlock(rwlock);
}

View File

@ -0,0 +1,389 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_WINDOWS
/**
* Semaphore functions using the Win32 API
* There are two implementations available based on:
* - Kernel Semaphores. Available on all OS versions. (kern)
* Heavy-weight inter-process kernel objects.
* - Atomics and WaitOnAddress API. (atom)
* Faster due to significantly less context switches.
* Requires Windows 8 or newer.
* which are chosen at runtime.
*/
#include "../../core/windows/SDL_windows.h"
typedef SDL_Semaphore *(*pfnSDL_CreateSemaphore)(Uint32);
typedef void (*pfnSDL_DestroySemaphore)(SDL_Semaphore *);
typedef int (*pfnSDL_WaitSemaphoreTimeoutNS)(SDL_Semaphore *, Sint64);
typedef Uint32 (*pfnSDL_GetSemaphoreValue)(SDL_Semaphore *);
typedef int (*pfnSDL_PostSemaphore)(SDL_Semaphore *);
typedef struct SDL_semaphore_impl_t
{
pfnSDL_CreateSemaphore Create;
pfnSDL_DestroySemaphore Destroy;
pfnSDL_WaitSemaphoreTimeoutNS WaitTimeoutNS;
pfnSDL_GetSemaphoreValue Value;
pfnSDL_PostSemaphore Post;
} SDL_sem_impl_t;
/* Implementation will be chosen at runtime based on available Kernel features */
static SDL_sem_impl_t SDL_sem_impl_active = { 0 };
/**
* Atomic + WaitOnAddress implementation
*/
/* APIs not available on WinPhone 8.1 */
/* https://www.microsoft.com/en-us/download/details.aspx?id=47328 */
#if !SDL_WINAPI_FAMILY_PHONE
#ifdef __WINRT__
/* Functions are guaranteed to be available */
#define pWaitOnAddress WaitOnAddress
#define pWakeByAddressSingle WakeByAddressSingle
#else
typedef BOOL(WINAPI *pfnWaitOnAddress)(volatile VOID *, PVOID, SIZE_T, DWORD);
typedef VOID(WINAPI *pfnWakeByAddressSingle)(PVOID);
static pfnWaitOnAddress pWaitOnAddress = NULL;
static pfnWakeByAddressSingle pWakeByAddressSingle = NULL;
#endif
typedef struct SDL_semaphore_atom
{
LONG count;
} SDL_sem_atom;
static SDL_Semaphore *SDL_CreateSemaphore_atom(Uint32 initial_value)
{
SDL_sem_atom *sem;
sem = (SDL_sem_atom *)SDL_malloc(sizeof(*sem));
if (sem != NULL) {
sem->count = initial_value;
} else {
SDL_OutOfMemory();
}
return (SDL_Semaphore *)sem;
}
static void SDL_DestroySemaphore_atom(SDL_Semaphore *sem)
{
if (sem != NULL) {
SDL_free(sem);
}
}
static int SDL_WaitSemaphoreTimeoutNS_atom(SDL_Semaphore *_sem, Sint64 timeoutNS)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
LONG count;
Uint64 now;
Uint64 deadline;
DWORD timeout_eff;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeoutNS == 0) {
count = sem->count;
if (count == 0) {
return SDL_MUTEX_TIMEDOUT;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return 0;
}
return SDL_MUTEX_TIMEDOUT;
}
if (timeoutNS < 0) {
for (;;) {
count = sem->count;
while (count == 0) {
if (pWaitOnAddress(&sem->count, &count, sizeof(sem->count), INFINITE) == FALSE) {
return SDL_SetError("WaitOnAddress() failed");
}
count = sem->count;
}
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return 0;
}
}
}
/**
* WaitOnAddress is subject to spurious and stolen wakeups so we
* need to recalculate the effective timeout before every wait
*/
now = SDL_GetTicksNS();
deadline = now + timeoutNS;
for (;;) {
count = sem->count;
/* If no semaphore is available we need to wait */
while (count == 0) {
now = SDL_GetTicksNS();
if (deadline > now) {
timeout_eff = (DWORD)SDL_NS_TO_MS(deadline - now);
} else {
return SDL_MUTEX_TIMEDOUT;
}
if (pWaitOnAddress(&sem->count, &count, sizeof(count), timeout_eff) == FALSE) {
if (GetLastError() == ERROR_TIMEOUT) {
return SDL_MUTEX_TIMEDOUT;
}
return SDL_SetError("WaitOnAddress() failed");
}
count = sem->count;
}
/* Actually the semaphore is only consumed if this succeeds */
/* If it doesn't we need to do everything again */
if (InterlockedCompareExchange(&sem->count, count - 1, count) == count) {
return 0;
}
}
}
static Uint32 SDL_GetSemaphoreValue_atom(SDL_Semaphore *_sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
return (Uint32)sem->count;
}
static int SDL_PostSemaphore_atom(SDL_Semaphore *_sem)
{
SDL_sem_atom *sem = (SDL_sem_atom *)_sem;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
InterlockedIncrement(&sem->count);
pWakeByAddressSingle(&sem->count);
return 0;
}
static const SDL_sem_impl_t SDL_sem_impl_atom = {
&SDL_CreateSemaphore_atom,
&SDL_DestroySemaphore_atom,
&SDL_WaitSemaphoreTimeoutNS_atom,
&SDL_GetSemaphoreValue_atom,
&SDL_PostSemaphore_atom,
};
#endif /* !SDL_WINAPI_FAMILY_PHONE */
/**
* Fallback Semaphore implementation using Kernel Semaphores
*/
typedef struct SDL_semaphore_kern
{
HANDLE id;
LONG count;
} SDL_sem_kern;
/* Create a semaphore */
static SDL_Semaphore *SDL_CreateSemaphore_kern(Uint32 initial_value)
{
SDL_sem_kern *sem;
/* Allocate sem memory */
sem = (SDL_sem_kern *)SDL_malloc(sizeof(*sem));
if (sem != NULL) {
/* Create the semaphore, with max value 32K */
#ifdef __WINRT__
sem->id = CreateSemaphoreEx(NULL, initial_value, 32 * 1024, NULL, 0, SEMAPHORE_ALL_ACCESS);
#else
sem->id = CreateSemaphore(NULL, initial_value, 32 * 1024, NULL);
#endif
sem->count = initial_value;
if (!sem->id) {
SDL_SetError("Couldn't create semaphore");
SDL_free(sem);
sem = NULL;
}
} else {
SDL_OutOfMemory();
}
return (SDL_Semaphore *)sem;
}
/* Free the semaphore */
static void SDL_DestroySemaphore_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (sem != NULL) {
if (sem->id) {
CloseHandle(sem->id);
sem->id = 0;
}
SDL_free(sem);
}
}
static int SDL_WaitSemaphoreTimeoutNS_kern(SDL_Semaphore *_sem, Sint64 timeoutNS)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
int retval;
DWORD dwMilliseconds;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
if (timeoutNS < 0) {
dwMilliseconds = INFINITE;
} else {
dwMilliseconds = (DWORD)SDL_NS_TO_MS(timeoutNS);
}
switch (WaitForSingleObjectEx(sem->id, dwMilliseconds, FALSE)) {
case WAIT_OBJECT_0:
InterlockedDecrement(&sem->count);
retval = 0;
break;
case WAIT_TIMEOUT:
retval = SDL_MUTEX_TIMEDOUT;
break;
default:
retval = SDL_SetError("WaitForSingleObject() failed");
break;
}
return retval;
}
/* Returns the current count of the semaphore */
static Uint32 SDL_GetSemaphoreValue_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (sem == NULL) {
SDL_InvalidParamError("sem");
return 0;
}
return (Uint32)sem->count;
}
static int SDL_PostSemaphore_kern(SDL_Semaphore *_sem)
{
SDL_sem_kern *sem = (SDL_sem_kern *)_sem;
if (sem == NULL) {
return SDL_InvalidParamError("sem");
}
/* Increase the counter in the first place, because
* after a successful release the semaphore may
* immediately get destroyed by another thread which
* is waiting for this semaphore.
*/
InterlockedIncrement(&sem->count);
if (ReleaseSemaphore(sem->id, 1, NULL) == FALSE) {
InterlockedDecrement(&sem->count); /* restore */
return SDL_SetError("ReleaseSemaphore() failed");
}
return 0;
}
static const SDL_sem_impl_t SDL_sem_impl_kern = {
&SDL_CreateSemaphore_kern,
&SDL_DestroySemaphore_kern,
&SDL_WaitSemaphoreTimeoutNS_kern,
&SDL_GetSemaphoreValue_kern,
&SDL_PostSemaphore_kern,
};
/**
* Runtime selection and redirection
*/
SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)
{
if (SDL_sem_impl_active.Create == NULL) {
/* Default to fallback implementation */
const SDL_sem_impl_t *impl = &SDL_sem_impl_kern;
#if !SDL_WINAPI_FAMILY_PHONE
if (!SDL_GetHintBoolean(SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL, SDL_FALSE)) {
#ifdef __WINRT__
/* Link statically on this platform */
impl = &SDL_sem_impl_atom;
#else
/* We already statically link to features from this Api
* Set (e.g. WaitForSingleObject). Dynamically loading
* API Sets is not explicitly documented but according to
* Microsoft our specific use case is legal and correct:
* https://github.com/microsoft/STL/pull/593#issuecomment-655799859
*/
HMODULE synch120 = GetModuleHandle(TEXT("api-ms-win-core-synch-l1-2-0.dll"));
if (synch120) {
/* Try to load required functions provided by Win 8 or newer */
pWaitOnAddress = (pfnWaitOnAddress)GetProcAddress(synch120, "WaitOnAddress");
pWakeByAddressSingle = (pfnWakeByAddressSingle)GetProcAddress(synch120, "WakeByAddressSingle");
if (pWaitOnAddress && pWakeByAddressSingle) {
impl = &SDL_sem_impl_atom;
}
}
#endif
}
#endif
/* Copy instead of using pointer to save one level of indirection */
SDL_copyp(&SDL_sem_impl_active, impl);
}
return SDL_sem_impl_active.Create(initial_value);
}
void SDL_DestroySemaphore(SDL_Semaphore *sem)
{
SDL_sem_impl_active.Destroy(sem);
}
int SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS)
{
return SDL_sem_impl_active.WaitTimeoutNS(sem, timeoutNS);
}
Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)
{
return SDL_sem_impl_active.Value(sem);
}
int SDL_PostSemaphore(SDL_Semaphore *sem)
{
return SDL_sem_impl_active.Post(sem);
}
#endif /* SDL_THREAD_WINDOWS */

View File

@ -0,0 +1,200 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_WINDOWS
/* Win32 thread management routines for SDL */
#include "../SDL_thread_c.h"
#include "../SDL_systhread.h"
#include "SDL_systhread_c.h"
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
#endif
#ifndef SDL_PASSED_BEGINTHREAD_ENDTHREAD
/* We'll use the C library from this DLL */
#include <process.h>
typedef uintptr_t(__cdecl *pfnSDL_CurrentBeginThread)(void *, unsigned,
unsigned(__stdcall *func)(void*),
void *arg, unsigned,
unsigned *threadID);
typedef void(__cdecl *pfnSDL_CurrentEndThread)(unsigned code);
#endif /* !SDL_PASSED_BEGINTHREAD_ENDTHREAD */
static DWORD RunThread(void *data)
{
SDL_Thread *thread = (SDL_Thread *)data;
pfnSDL_CurrentEndThread pfnEndThread = (pfnSDL_CurrentEndThread)thread->endfunc;
SDL_RunThread(thread);
if (pfnEndThread != NULL) {
pfnEndThread(0);
}
return 0;
}
static DWORD WINAPI RunThreadViaCreateThread(LPVOID data)
{
return RunThread(data);
}
static unsigned __stdcall RunThreadViaBeginThreadEx(void *data)
{
return (unsigned)RunThread(data);
}
#ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
int SDL_SYS_CreateThread(SDL_Thread *thread,
pfnSDL_CurrentBeginThread pfnBeginThread,
pfnSDL_CurrentEndThread pfnEndThread)
{
#elif defined(__CYGWIN__) || defined(__WINRT__)
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
pfnSDL_CurrentBeginThread pfnBeginThread = NULL;
pfnSDL_CurrentEndThread pfnEndThread = NULL;
#else
int SDL_SYS_CreateThread(SDL_Thread *thread)
{
pfnSDL_CurrentBeginThread pfnBeginThread = (pfnSDL_CurrentBeginThread)_beginthreadex;
pfnSDL_CurrentEndThread pfnEndThread = (pfnSDL_CurrentEndThread)_endthreadex;
#endif /* SDL_PASSED_BEGINTHREAD_ENDTHREAD */
const DWORD flags = thread->stacksize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0;
/* Save the function which we will have to call to clear the RTL of calling app! */
thread->endfunc = pfnEndThread;
/* thread->stacksize == 0 means "system default", same as win32 expects */
if (pfnBeginThread) {
unsigned threadid = 0;
thread->handle = (SYS_ThreadHandle)((size_t)pfnBeginThread(NULL, (unsigned int)thread->stacksize,
RunThreadViaBeginThreadEx,
thread, flags, &threadid));
} else {
DWORD threadid = 0;
thread->handle = CreateThread(NULL, thread->stacksize,
RunThreadViaCreateThread,
thread, flags, &threadid);
}
if (thread->handle == NULL) {
return SDL_SetError("Not enough resources to create thread");
}
return 0;
}
#pragma pack(push, 8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; /* must be 0x1000 */
LPCSTR szName; /* pointer to name (in user addr space) */
DWORD dwThreadID; /* thread ID (-1=caller thread) */
DWORD dwFlags; /* reserved for future use, must be zero */
} THREADNAME_INFO;
#pragma pack(pop)
typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE, PCWSTR);
void SDL_SYS_SetupThread(const char *name)
{
if (name != NULL) {
#ifndef __WINRT__ /* !!! FIXME: There's no LoadLibrary() in WinRT; don't know if SetThreadDescription is available there at all at the moment. */
static pfnSetThreadDescription pSetThreadDescription = NULL;
static HMODULE kernel32 = NULL;
if (!kernel32) {
kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
if (kernel32) {
pSetThreadDescription = (pfnSetThreadDescription)GetProcAddress(kernel32, "SetThreadDescription");
}
}
if (pSetThreadDescription != NULL) {
WCHAR *strw = WIN_UTF8ToStringW(name);
if (strw) {
pSetThreadDescription(GetCurrentThread(), strw);
SDL_free(strw);
}
}
#endif
/* Presumably some version of Visual Studio will understand SetThreadDescription(),
but we still need to deal with older OSes and debuggers. Set it with the arcane
exception magic, too. */
if (IsDebuggerPresent()) {
THREADNAME_INFO inf;
/* C# and friends will try to catch this Exception, let's avoid it. */
if (SDL_GetHintBoolean(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, SDL_TRUE)) {
return;
}
/* This magic tells the debugger to name a thread if it's listening. */
SDL_zero(inf);
inf.dwType = 0x1000;
inf.szName = name;
inf.dwThreadID = (DWORD)-1;
inf.dwFlags = 0;
/* The debugger catches this, renames the thread, continues on. */
RaiseException(0x406D1388, 0, sizeof(inf) / sizeof(ULONG), (const ULONG_PTR *)&inf);
}
}
}
SDL_threadID SDL_ThreadID(void)
{
return (SDL_threadID)GetCurrentThreadId();
}
int SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
{
int value;
if (priority == SDL_THREAD_PRIORITY_LOW) {
value = THREAD_PRIORITY_LOWEST;
} else if (priority == SDL_THREAD_PRIORITY_HIGH) {
value = THREAD_PRIORITY_HIGHEST;
} else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
value = THREAD_PRIORITY_TIME_CRITICAL;
} else {
value = THREAD_PRIORITY_NORMAL;
}
if (!SetThreadPriority(GetCurrentThread(), value)) {
return WIN_SetError("SetThreadPriority()");
}
return 0;
}
void SDL_SYS_WaitThread(SDL_Thread *thread)
{
WaitForSingleObjectEx(thread->handle, INFINITE, FALSE);
CloseHandle(thread->handle);
}
void SDL_SYS_DetachThread(SDL_Thread *thread)
{
CloseHandle(thread->handle);
}
#endif /* SDL_THREAD_WINDOWS */

View File

@ -0,0 +1,30 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifndef SDL_systhread_c_h_
#define SDL_systhread_c_h_
#include "../../core/windows/SDL_windows.h"
typedef HANDLE SYS_ThreadHandle;
#endif /* SDL_systhread_c_h_ */

View File

@ -0,0 +1,79 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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"
#ifdef SDL_THREAD_WINDOWS
#include "../../core/windows/SDL_windows.h"
#include "../SDL_thread_c.h"
#if WINAPI_FAMILY_WINRT
#include <fibersapi.h>
#ifndef TLS_OUT_OF_INDEXES
#define TLS_OUT_OF_INDEXES FLS_OUT_OF_INDEXES
#endif
#define TlsAlloc() FlsAlloc(NULL)
#define TlsSetValue FlsSetValue
#define TlsGetValue FlsGetValue
#endif
static DWORD thread_local_storage = TLS_OUT_OF_INDEXES;
static SDL_bool generic_local_storage = SDL_FALSE;
SDL_TLSData *SDL_SYS_GetTLSData(void)
{
if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) {
static SDL_SpinLock lock;
SDL_AtomicLock(&lock);
if (thread_local_storage == TLS_OUT_OF_INDEXES && !generic_local_storage) {
DWORD storage = TlsAlloc();
if (storage != TLS_OUT_OF_INDEXES) {
SDL_MemoryBarrierRelease();
thread_local_storage = storage;
} else {
generic_local_storage = SDL_TRUE;
}
}
SDL_AtomicUnlock(&lock);
}
if (generic_local_storage) {
return SDL_Generic_GetTLSData();
}
SDL_MemoryBarrierAcquire();
return (SDL_TLSData *)TlsGetValue(thread_local_storage);
}
int SDL_SYS_SetTLSData(SDL_TLSData *data)
{
if (generic_local_storage) {
return SDL_Generic_SetTLSData(data);
}
if (!TlsSetValue(thread_local_storage, data)) {
return SDL_SetError("TlsSetValue() failed");
}
return 0;
}
#endif /* SDL_THREAD_WINDOWS */