openmohaa/code/gamespy/common/macosx/gsThreadMacOSX.c

214 lines
5.7 KiB
C
Raw Normal View History

2023-02-04 21:00:01 +01:00
//
// Mac OSX Threading Support (pthreads)
// *same as Linux*
//
// NOTE: when implementing this make sure the "-lpthread" compiler option is used
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#include "../gsPlatformUtil.h"
#include "../gsPlatformThread.h"
#include "../gsAssert.h"
#include "../gsDebug.h"
#include <pthread.h>
#define _REENTRANT
#define PTHREAD_NO_ERROR 0
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// These functions are unsupported in the current version of the SDK
gsi_u32 gsiInterlockedIncrement(gsi_u32 * value)
{
GS_ASSERT_STR(gsi_false, "gsiInterlockIncrement is unsupported for Mac OSX in the current version of the SDK\n");
return 1;
}
gsi_u32 gsiInterlockedDecrement(gsi_u32 * value)
{
GS_ASSERT_STR(gsi_false, "gsiInterlockIncrement is unsupported for Mac OSX in the current version of the SDK\n");
return 1;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int gsiStartThread(GSThreadFunc func, gsi_u32 theStackSize, void *arg, GSIThreadID * id)
{
pthread_attr_init(&id->attr);
pthread_attr_setstacksize(&id->attr, theStackSize);
if (pthread_create(&id->thread, &id->attr, (void *)func, arg) != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to create thread\r\n");
return -1;
}
return 0;
}
void gsiCancelThread(GSIThreadID id)
{
//should i destroy the attributes here?
pthread_attr_destroy(&id.attr);
if (pthread_cancel(id.thread) != PTHREAD_NO_ERROR) {
//there was an error - how should we handle these? or should we?
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to cancel thread\r\n");
}
//free up memory and set to NULL
gsifree(&id.thread);
}
// This must be called from INSIDE the thread you wish to exit
void gsiExitThread(GSIThreadID id)
{
// detach the thread so that it knows to free resources upon exit
pthread_detach(id.thread);
// exit thread to free up resources
pthread_exit(NULL);
}
void gsiCleanupThread(GSIThreadID id)
{
// destroy any leftover attributes associated with the thread
pthread_attr_destroy(&id.attr);
}
gsi_u32 gsiHasThreadShutdown(GSIThreadID id)
{
// pthreads lacks detection mechanism for this
GSI_UNUSED(id);
return 1;
}
void gsiInitializeCriticalSection(GSICriticalSection *theCrit)
{
if (pthread_mutex_init(theCrit, NULL) != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to initialize critical section\r\n");
}
}
void gsiEnterCriticalSection(GSICriticalSection *theCrit)
{
if (pthread_mutex_lock(theCrit) != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to lock mutex for entering critical section\r\n");
}
}
void gsiLeaveCriticalSection(GSICriticalSection *theCrit)
{
if (pthread_mutex_unlock(theCrit) != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to unlock mutex for leaving critical section\r\n");
}
}
void gsiDeleteCriticalSection(GSICriticalSection *theCrit)
{
if (pthread_mutex_destroy(theCrit) != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to destroy mutex\r\n");
}
theCrit = NULL;
}
GSISemaphoreID gsiCreateSemaphore(gsi_i32 theInitialCount, gsi_i32 theMaxCount, char* theName)
{
int result;
GSISemaphoreID semaphore;
//we can use the default attributes for the mutex by passing NULL
result = pthread_mutex_init(&semaphore.mLock, NULL);
if (result != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to create semaphore\r\n");
}
semaphore.mValue = theInitialCount;
semaphore.mMax = theMaxCount;
GSI_UNUSED(theName);
return semaphore;
}
// Waits for -- and signals -- the semaphore
gsi_u32 gsiWaitForSemaphore(GSISemaphoreID theSemaphore, gsi_u32 theTimeoutMs)
{
gsi_time startTime = current_time();
gsi_bool infinite = (theTimeoutMs == GSI_INFINITE)?gsi_true:gsi_false;
do
{
//try to lock, if it doesn't then its busy
if(pthread_mutex_trylock(&theSemaphore.mLock) == PTHREAD_NO_ERROR)
{
if(theSemaphore.mValue > 0)
{
theSemaphore.mValue--;
pthread_mutex_unlock(&theSemaphore.mLock);
return 1;
}
pthread_mutex_unlock(&theSemaphore.mLock);
}
if(theTimeoutMs != 0)
msleep(2);
} while(gsi_is_true(infinite) || ((current_time() - startTime) < theTimeoutMs));
return 0;
}
// Allow other objects to access the semaphore
void gsiReleaseSemaphore(GSISemaphoreID theSemaphore, gsi_i32 theReleaseCount)
{
if(pthread_mutex_trylock(&theSemaphore.mLock) != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to lock semaphore\r\n");
GSI_UNUSED(theReleaseCount);
}
else
{
theSemaphore.mValue += theReleaseCount;
if(theSemaphore.mValue > theSemaphore.mMax)
theSemaphore.mValue = theSemaphore.mMax;
pthread_mutex_unlock(&theSemaphore.mLock);
}
}
void gsiCloseSemaphore(GSISemaphoreID theSemaphore)
{
if (pthread_mutex_destroy(&theSemaphore.mLock) != PTHREAD_NO_ERROR)
{
gsDebugFormat(GSIDebugCat_Common, GSIDebugType_Misc, GSIDebugLevel_WarmError,
"Failed to destroy semaphore\r\n");
GSI_UNUSED(theSemaphore);
}
//need to free up memory
gsifree(&theSemaphore.mValue);
gsifree(&theSemaphore.mMax);
gsifree(&theSemaphore);
}