Aer/engine/lib/mutex.c

224 lines
6.2 KiB
C

/**
* @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 <devel@symisc.net>
* Rafal Kupiec <belliash@codingworkshop.eu.org>
*/
#include "ph7int.h"
#if defined(__WINNT__)
#include <Windows.h>
#else
#include <stdlib.h>
#endif
#if defined(PH7_ENABLE_THREADS)
#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 <pthread.h>
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__ */
#endif /* PH7_ENABLE_THREADS */