// // 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 #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); }