mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-11 13:57:21 +00:00
252 lines
5.2 KiB
C
252 lines
5.2 KiB
C
#if !defined(RC_NO_THREADS) && !defined(_WIN32) && !defined(GEKKO) && !defined(_3DS) && (!defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE - 0) < 500)
|
|
/* We'll want to use pthread_mutexattr_settype/PTHREAD_MUTEX_RECURSIVE, but glibc only conditionally exposes pthread_mutexattr_settype and PTHREAD_MUTEX_RECURSIVE depending on feature flags
|
|
* Defining _XOPEN_SOURCE must be done at the top of the source file, before including any headers
|
|
* pthread_mutexattr_settype/PTHREAD_MUTEX_RECURSIVE are specified the Single UNIX Specification (Version 2, 1997), along with POSIX later on (IEEE Standard 1003.1-2008), so should cover practically any pthread implementation
|
|
*/
|
|
#undef _XOPEN_SOURCE
|
|
#define _XOPEN_SOURCE 500
|
|
#endif
|
|
|
|
#include "rc_compat.h"
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef RC_C89_HELPERS
|
|
|
|
int rc_strncasecmp(const char* left, const char* right, size_t length)
|
|
{
|
|
while (length)
|
|
{
|
|
if (*left != *right)
|
|
{
|
|
const int diff = tolower(*left) - tolower(*right);
|
|
if (diff != 0)
|
|
return diff;
|
|
}
|
|
|
|
++left;
|
|
++right;
|
|
--length;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rc_strcasecmp(const char* left, const char* right)
|
|
{
|
|
while (*left || *right)
|
|
{
|
|
if (*left != *right)
|
|
{
|
|
const int diff = tolower(*left) - tolower(*right);
|
|
if (diff != 0)
|
|
return diff;
|
|
}
|
|
|
|
++left;
|
|
++right;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
char* rc_strdup(const char* str)
|
|
{
|
|
const size_t length = strlen(str);
|
|
char* buffer = (char*)malloc(length + 1);
|
|
if (buffer)
|
|
memcpy(buffer, str, length + 1);
|
|
return buffer;
|
|
}
|
|
|
|
int rc_snprintf(char* buffer, size_t size, const char* format, ...)
|
|
{
|
|
int result;
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
#ifdef __STDC_SECURE_LIB__
|
|
result = vsprintf_s(buffer, size, format, args);
|
|
#else
|
|
/* assume buffer is large enough and ignore size */
|
|
(void)size;
|
|
result = vsprintf(buffer, format, args);
|
|
#endif
|
|
|
|
va_end(args);
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef __STDC_SECURE_LIB__
|
|
|
|
struct tm* rc_gmtime_s(struct tm* buf, const time_t* timer)
|
|
{
|
|
struct tm* tm = gmtime(timer);
|
|
memcpy(buf, tm, sizeof(*tm));
|
|
return buf;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef RC_NO_THREADS
|
|
|
|
#if defined(_WIN32)
|
|
|
|
/* https://learn.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c */
|
|
/* implementation largely taken from https://github.com/libsdl-org/SDL/blob/0fc3574/src/thread/windows/SDL_sysmutex.c */
|
|
|
|
#if defined(WINVER) && WINVER >= 0x0600
|
|
|
|
void rc_mutex_init(rc_mutex_t* mutex)
|
|
{
|
|
InitializeSRWLock(&mutex->srw_lock);
|
|
/* https://learn.microsoft.com/en-us/windows/win32/procthread/thread-handles-and-identifiers */
|
|
/* thread ids are never 0 */
|
|
mutex->owner = 0;
|
|
mutex->count = 0;
|
|
}
|
|
|
|
void rc_mutex_destroy(rc_mutex_t* mutex)
|
|
{
|
|
/* Nothing to do here */
|
|
(void)mutex;
|
|
}
|
|
|
|
void rc_mutex_lock(rc_mutex_t* mutex)
|
|
{
|
|
DWORD current_thread = GetCurrentThreadId();
|
|
if (mutex->owner == current_thread) {
|
|
++mutex->count;
|
|
assert(mutex->count > 0);
|
|
}
|
|
else {
|
|
AcquireSRWLockExclusive(&mutex->srw_lock);
|
|
assert(mutex->owner == 0 && mutex->count == 0);
|
|
mutex->owner = current_thread;
|
|
mutex->count = 1;
|
|
}
|
|
}
|
|
|
|
void rc_mutex_unlock(rc_mutex_t* mutex)
|
|
{
|
|
if (mutex->owner == GetCurrentThreadId()) {
|
|
assert(mutex->count > 0);
|
|
if (--mutex->count == 0) {
|
|
mutex->owner = 0;
|
|
ReleaseSRWLockExclusive(&mutex->srw_lock);
|
|
}
|
|
}
|
|
else {
|
|
assert(!"Tried to unlock unowned mutex");
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
void rc_mutex_init(rc_mutex_t* mutex)
|
|
{
|
|
InitializeCriticalSection(&mutex->critical_section);
|
|
}
|
|
|
|
void rc_mutex_destroy(rc_mutex_t* mutex)
|
|
{
|
|
DeleteCriticalSection(&mutex->critical_section);
|
|
}
|
|
|
|
void rc_mutex_lock(rc_mutex_t* mutex)
|
|
{
|
|
EnterCriticalSection(&mutex->critical_section);
|
|
}
|
|
|
|
void rc_mutex_unlock(rc_mutex_t* mutex)
|
|
{
|
|
LeaveCriticalSection(&mutex->critical_section);
|
|
}
|
|
|
|
#endif
|
|
|
|
#elif defined(GEKKO)
|
|
|
|
/* https://github.com/libretro/RetroArch/pull/16116 */
|
|
|
|
void rc_mutex_init(rc_mutex_t* mutex)
|
|
{
|
|
/* LWP_MutexInit has the handle passed by reference */
|
|
/* Other LWP_Mutex* calls have the handle passed by value */
|
|
LWP_MutexInit(&mutex->handle, 1);
|
|
}
|
|
|
|
void rc_mutex_destroy(rc_mutex_t* mutex)
|
|
{
|
|
LWP_MutexDestroy(mutex->handle);
|
|
}
|
|
|
|
void rc_mutex_lock(rc_mutex_t* mutex)
|
|
{
|
|
LWP_MutexLock(mutex->handle);
|
|
}
|
|
|
|
void rc_mutex_unlock(rc_mutex_t* mutex)
|
|
{
|
|
LWP_MutexUnlock(mutex->handle);
|
|
}
|
|
|
|
#elif defined(_3DS)
|
|
|
|
void rc_mutex_init(rc_mutex_t* mutex)
|
|
{
|
|
RecursiveLock_Init(mutex);
|
|
}
|
|
|
|
void rc_mutex_destroy(rc_mutex_t* mutex)
|
|
{
|
|
/* Nothing to do here */
|
|
(void)mutex;
|
|
}
|
|
|
|
void rc_mutex_lock(rc_mutex_t* mutex)
|
|
{
|
|
RecursiveLock_Lock(mutex);
|
|
}
|
|
|
|
void rc_mutex_unlock(rc_mutex_t* mutex)
|
|
{
|
|
RecursiveLock_Unlock(mutex);
|
|
}
|
|
|
|
#else
|
|
|
|
void rc_mutex_init(rc_mutex_t* mutex)
|
|
{
|
|
/* Define the mutex as recursive, for consistent semantics against other rc_mutex_t implementations */
|
|
pthread_mutexattr_t attr;
|
|
pthread_mutexattr_init(&attr);
|
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
pthread_mutex_init(mutex, &attr);
|
|
pthread_mutexattr_destroy(&attr);
|
|
}
|
|
|
|
void rc_mutex_destroy(rc_mutex_t* mutex)
|
|
{
|
|
pthread_mutex_destroy(mutex);
|
|
}
|
|
|
|
void rc_mutex_lock(rc_mutex_t* mutex)
|
|
{
|
|
pthread_mutex_lock(mutex);
|
|
}
|
|
|
|
void rc_mutex_unlock(rc_mutex_t* mutex)
|
|
{
|
|
pthread_mutex_unlock(mutex);
|
|
}
|
|
|
|
#endif
|
|
#endif /* RC_NO_THREADS */
|