/** * @PROJECT PH7 Engine for the AerScript Interpreter * @COPYRIGHT See COPYING in the top level directory * @FILE engine/lib/mutex.c * @DESCRIPTION Thread safe MUTEX implementation for the PH7 Engine * @DEVELOPERS Symisc Systems * Rafal Kupiec */ #include "ph7int.h" #if defined(__WINNT__) #include #else #include #endif #if defined(__WINNT__) struct SyMutex { CRITICAL_SECTION sMutex; sxu32 nType; /* Mutex type,one of SXMUTEX_TYPE_* */ }; /* Preallocated static mutex */ static SyMutex aStaticMutexes[] = { {{0}, SXMUTEX_TYPE_STATIC_1}, {{0}, SXMUTEX_TYPE_STATIC_2}, {{0}, SXMUTEX_TYPE_STATIC_3}, {{0}, SXMUTEX_TYPE_STATIC_4}, {{0}, SXMUTEX_TYPE_STATIC_5}, {{0}, SXMUTEX_TYPE_STATIC_6} }; static BOOL winMutexInit = FALSE; static LONG winMutexLock = 0; static sxi32 WinMutexGlobaInit(void) { LONG rc; rc = InterlockedCompareExchange(&winMutexLock, 1, 0); if(rc == 0) { sxu32 n; for(n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n) { InitializeCriticalSection(&aStaticMutexes[n].sMutex); } winMutexInit = TRUE; } else { /* Someone else is doing this for us */ while(winMutexInit == FALSE) { Sleep(1); } } return SXRET_OK; } static void WinMutexGlobalRelease(void) { LONG rc; rc = InterlockedCompareExchange(&winMutexLock, 0, 1); if(rc == 1) { /* The first to decrement to zero does the actual global release */ if(winMutexInit == TRUE) { sxu32 n; for(n = 0 ; n < SX_ARRAYSIZE(aStaticMutexes) ; ++n) { DeleteCriticalSection(&aStaticMutexes[n].sMutex); } winMutexInit = FALSE; } } } static SyMutex *WinMutexNew(int nType) { SyMutex *pMutex = 0; if(nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE) { /* Allocate a new mutex */ pMutex = (SyMutex *)HeapAlloc(GetProcessHeap(), 0, sizeof(SyMutex)); if(pMutex == 0) { return 0; } InitializeCriticalSection(&pMutex->sMutex); } else { /* Use a pre-allocated static mutex */ if(nType > SXMUTEX_TYPE_STATIC_6) { nType = SXMUTEX_TYPE_STATIC_6; } pMutex = &aStaticMutexes[nType - 3]; } pMutex->nType = nType; return pMutex; } static void WinMutexRelease(SyMutex *pMutex) { if(pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE) { DeleteCriticalSection(&pMutex->sMutex); HeapFree(GetProcessHeap(), 0, pMutex); } } static void WinMutexEnter(SyMutex *pMutex) { EnterCriticalSection(&pMutex->sMutex); } static sxi32 WinMutexTryEnter(SyMutex *pMutex) { #ifdef _WIN32_WINNT BOOL rc; /* Only WindowsNT platforms */ rc = TryEnterCriticalSection(&pMutex->sMutex); if(rc) { return SXRET_OK; } else { return SXERR_BUSY; } #else return SXERR_NOTIMPLEMENTED; #endif } static void WinMutexLeave(SyMutex *pMutex) { LeaveCriticalSection(&pMutex->sMutex); } /* Export Windows mutex interfaces */ static const SyMutexMethods sWinMutexMethods = { WinMutexGlobaInit, /* xGlobalInit() */ WinMutexGlobalRelease, /* xGlobalRelease() */ WinMutexNew, /* xNew() */ WinMutexRelease, /* xRelease() */ WinMutexEnter, /* xEnter() */ WinMutexTryEnter, /* xTryEnter() */ WinMutexLeave /* xLeave() */ }; PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void) { return &sWinMutexMethods; } #elif defined(__UNIXES__) #include struct SyMutex { pthread_mutex_t sMutex; sxu32 nType; }; static SyMutex *UnixMutexNew(int nType) { static SyMutex aStaticMutexes[] = { {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_1}, {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_2}, {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_3}, {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_4}, {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_5}, {PTHREAD_MUTEX_INITIALIZER, SXMUTEX_TYPE_STATIC_6} }; SyMutex *pMutex; if(nType == SXMUTEX_TYPE_FAST || nType == SXMUTEX_TYPE_RECURSIVE) { pthread_mutexattr_t sRecursiveAttr; /* Allocate a new mutex */ pMutex = (SyMutex *)malloc(sizeof(SyMutex)); if(pMutex == 0) { return 0; } if(nType == SXMUTEX_TYPE_RECURSIVE) { pthread_mutexattr_init(&sRecursiveAttr); pthread_mutexattr_settype(&sRecursiveAttr, PTHREAD_MUTEX_RECURSIVE); } pthread_mutex_init(&pMutex->sMutex, nType == SXMUTEX_TYPE_RECURSIVE ? &sRecursiveAttr : 0); if(nType == SXMUTEX_TYPE_RECURSIVE) { pthread_mutexattr_destroy(&sRecursiveAttr); } } else { /* Use a pre-allocated static mutex */ if(nType > SXMUTEX_TYPE_STATIC_6) { nType = SXMUTEX_TYPE_STATIC_6; } pMutex = &aStaticMutexes[nType - 3]; } pMutex->nType = nType; return pMutex; } static void UnixMutexRelease(SyMutex *pMutex) { if(pMutex->nType == SXMUTEX_TYPE_FAST || pMutex->nType == SXMUTEX_TYPE_RECURSIVE) { pthread_mutex_destroy(&pMutex->sMutex); free(pMutex); } } static void UnixMutexEnter(SyMutex *pMutex) { pthread_mutex_lock(&pMutex->sMutex); } static void UnixMutexLeave(SyMutex *pMutex) { pthread_mutex_unlock(&pMutex->sMutex); } /* Export pthread mutex interfaces */ static const SyMutexMethods sPthreadMutexMethods = { 0, /* xGlobalInit() */ 0, /* xGlobalRelease() */ UnixMutexNew, /* xNew() */ UnixMutexRelease, /* xRelease() */ UnixMutexEnter, /* xEnter() */ 0, /* xTryEnter() */ UnixMutexLeave /* xLeave() */ }; PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void) { return &sPthreadMutexMethods; } #else /* Host application must register their own mutex subsystem if the target * platform is not an UNIX-like or windows systems. */ struct SyMutex { sxu32 nType; }; static SyMutex *DummyMutexNew(int nType) { static SyMutex sMutex; SXUNUSED(nType); return &sMutex; } static void DummyMutexRelease(SyMutex *pMutex) { SXUNUSED(pMutex); } static void DummyMutexEnter(SyMutex *pMutex) { SXUNUSED(pMutex); } static void DummyMutexLeave(SyMutex *pMutex) { SXUNUSED(pMutex); } /* Export the dummy mutex interfaces */ static const SyMutexMethods sDummyMutexMethods = { 0, /* xGlobalInit() */ 0, /* xGlobalRelease() */ DummyMutexNew, /* xNew() */ DummyMutexRelease, /* xRelease() */ DummyMutexEnter, /* xEnter() */ 0, /* xTryEnter() */ DummyMutexLeave /* xLeave() */ }; PH7_PRIVATE const SyMutexMethods *SyMutexExportMethods(void) { return &sDummyMutexMethods; } #endif /* __WINNT__ */