d847a21238
X-SVN-Rev: 40283
379 lines
11 KiB
C++
379 lines
11 KiB
C++
// © 2016 and later: Unicode, Inc. and others.
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
/*
|
|
******************************************************************************
|
|
*
|
|
* Copyright (C) 1997-2016, International Business Machines
|
|
* Corporation and others. All Rights Reserved.
|
|
*
|
|
******************************************************************************
|
|
*
|
|
* File umutex.cpp
|
|
*
|
|
* Modification History:
|
|
*
|
|
* Date Name Description
|
|
* 04/02/97 aliu Creation.
|
|
* 04/07/99 srl updated
|
|
* 05/13/99 stephen Changed to umutex (from cmutex).
|
|
* 11/22/99 aliu Make non-global mutex autoinitialize [j151]
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "umutex.h"
|
|
|
|
#include "unicode/utypes.h"
|
|
#include "uassert.h"
|
|
#include "cmemory.h"
|
|
|
|
|
|
// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
|
|
static UMutex globalMutex = U_MUTEX_INITIALIZER;
|
|
|
|
/*
|
|
* ICU Mutex wrappers. Wrap operating system mutexes, giving the rest of ICU a
|
|
* platform independent set of mutex operations. For internal ICU use only.
|
|
*/
|
|
|
|
#if defined(U_USER_MUTEX_CPP)
|
|
// Build time user mutex hook: #include "U_USER_MUTEX_CPP"
|
|
#include U_MUTEX_XSTR(U_USER_MUTEX_CPP)
|
|
|
|
#elif U_PLATFORM_USES_ONLY_WIN32_API
|
|
|
|
#if defined U_NO_PLATFORM_ATOMICS
|
|
#error ICU on Win32 requires support for low level atomic operations.
|
|
// Visual Studio, gcc, clang are OK. Shouldn't get here.
|
|
#endif
|
|
|
|
|
|
// This function is called when a test of a UInitOnce::fState reveals that
|
|
// initialization has not completed, that we either need to call the
|
|
// function on this thread, or wait for some other thread to complete.
|
|
//
|
|
// The actual call to the init function is made inline by template code
|
|
// that knows the C++ types involved. This function returns TRUE if
|
|
// the caller needs to call the Init function.
|
|
//
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
|
|
for (;;) {
|
|
int32_t previousState = InterlockedCompareExchange(
|
|
(LONG volatile *) // this is the type given in the API doc for this function.
|
|
&uio.fState, // Destination
|
|
1, // Exchange Value
|
|
0); // Compare value
|
|
|
|
if (previousState == 0) {
|
|
return true; // Caller will next call the init function.
|
|
// Current state == 1.
|
|
} else if (previousState == 2) {
|
|
// Another thread already completed the initialization.
|
|
// We can simply return FALSE, indicating no
|
|
// further action is needed by the caller.
|
|
return FALSE;
|
|
} else {
|
|
// Another thread is currently running the initialization.
|
|
// Wait until it completes.
|
|
do {
|
|
Sleep(1);
|
|
previousState = umtx_loadAcquire(uio.fState);
|
|
} while (previousState == 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This function is called by the thread that ran an initialization function,
|
|
// just after completing the function.
|
|
|
|
U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
|
|
umtx_storeRelease(uio.fState, 2);
|
|
}
|
|
|
|
U_NAMESPACE_END
|
|
|
|
static void winMutexInit(CRITICAL_SECTION *cs) {
|
|
InitializeCriticalSection(cs);
|
|
return;
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_lock(UMutex *mutex) {
|
|
if (mutex == NULL) {
|
|
mutex = &globalMutex;
|
|
}
|
|
CRITICAL_SECTION *cs = &mutex->fCS;
|
|
umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
|
|
EnterCriticalSection(cs);
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_unlock(UMutex* mutex)
|
|
{
|
|
if (mutex == NULL) {
|
|
mutex = &globalMutex;
|
|
}
|
|
LeaveCriticalSection(&mutex->fCS);
|
|
}
|
|
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_condBroadcast(UConditionVar *condition) {
|
|
// We require that the associated mutex be held by the caller,
|
|
// so access to fWaitCount is protected and safe. No other thread can
|
|
// call condWait() while we are here.
|
|
if (condition->fWaitCount == 0) {
|
|
return;
|
|
}
|
|
ResetEvent(condition->fExitGate);
|
|
SetEvent(condition->fEntryGate);
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_condSignal(UConditionVar * /* condition */) {
|
|
// Function not implemented. There is no immediate requirement from ICU to have it.
|
|
// Once ICU drops support for Windows XP and Server 2003, ICU Condition Variables will be
|
|
// changed to be thin wrappers on native Windows CONDITION_VARIABLEs, and this function
|
|
// becomes trivial to provide.
|
|
U_ASSERT(FALSE);
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_condWait(UConditionVar *condition, UMutex *mutex) {
|
|
if (condition->fEntryGate == NULL) {
|
|
// Note: because the associated mutex must be locked when calling
|
|
// wait, we know that there can not be multiple threads
|
|
// running here with the same condition variable.
|
|
// Meaning that lazy initialization is safe.
|
|
U_ASSERT(condition->fExitGate == NULL);
|
|
condition->fEntryGate = CreateEvent(NULL, // Security Attributes
|
|
TRUE, // Manual Reset
|
|
FALSE, // Initially reset
|
|
NULL); // Name.
|
|
U_ASSERT(condition->fEntryGate != NULL);
|
|
condition->fExitGate = CreateEvent(NULL, TRUE, TRUE, NULL);
|
|
U_ASSERT(condition->fExitGate != NULL);
|
|
}
|
|
|
|
condition->fWaitCount++;
|
|
umtx_unlock(mutex);
|
|
WaitForSingleObject(condition->fEntryGate, INFINITE);
|
|
umtx_lock(mutex);
|
|
condition->fWaitCount--;
|
|
if (condition->fWaitCount == 0) {
|
|
// All threads that were waiting at the entry gate have woken up
|
|
// and moved through. Shut the entry gate and open the exit gate.
|
|
ResetEvent(condition->fEntryGate);
|
|
SetEvent(condition->fExitGate);
|
|
} else {
|
|
umtx_unlock(mutex);
|
|
WaitForSingleObject(condition->fExitGate, INFINITE);
|
|
umtx_lock(mutex);
|
|
}
|
|
}
|
|
|
|
|
|
#elif U_PLATFORM_IMPLEMENTS_POSIX
|
|
|
|
//-------------------------------------------------------------------------------------------
|
|
//
|
|
// POSIX specific definitions
|
|
//
|
|
//-------------------------------------------------------------------------------------------
|
|
|
|
# include <pthread.h>
|
|
|
|
// Each UMutex consists of a pthread_mutex_t.
|
|
// All are statically initialized and ready for use.
|
|
// There is no runtime mutex initialization code needed.
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_lock(UMutex *mutex) {
|
|
if (mutex == NULL) {
|
|
mutex = &globalMutex;
|
|
}
|
|
int sysErr = pthread_mutex_lock(&mutex->fMutex);
|
|
(void)sysErr; // Suppress unused variable warnings.
|
|
U_ASSERT(sysErr == 0);
|
|
}
|
|
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_unlock(UMutex* mutex)
|
|
{
|
|
if (mutex == NULL) {
|
|
mutex = &globalMutex;
|
|
}
|
|
int sysErr = pthread_mutex_unlock(&mutex->fMutex);
|
|
(void)sysErr; // Suppress unused variable warnings.
|
|
U_ASSERT(sysErr == 0);
|
|
}
|
|
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_condWait(UConditionVar *cond, UMutex *mutex) {
|
|
if (mutex == NULL) {
|
|
mutex = &globalMutex;
|
|
}
|
|
int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex);
|
|
(void)sysErr;
|
|
U_ASSERT(sysErr == 0);
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_condBroadcast(UConditionVar *cond) {
|
|
int sysErr = pthread_cond_broadcast(&cond->fCondition);
|
|
(void)sysErr;
|
|
U_ASSERT(sysErr == 0);
|
|
}
|
|
|
|
U_CAPI void U_EXPORT2
|
|
umtx_condSignal(UConditionVar *cond) {
|
|
int sysErr = pthread_cond_signal(&cond->fCondition);
|
|
(void)sysErr;
|
|
U_ASSERT(sysErr == 0);
|
|
}
|
|
|
|
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
|
|
|
|
|
|
// This function is called when a test of a UInitOnce::fState reveals that
|
|
// initialization has not completed, that we either need to call the
|
|
// function on this thread, or wait for some other thread to complete.
|
|
//
|
|
// The actual call to the init function is made inline by template code
|
|
// that knows the C++ types involved. This function returns TRUE if
|
|
// the caller needs to call the Init function.
|
|
//
|
|
U_COMMON_API UBool U_EXPORT2
|
|
umtx_initImplPreInit(UInitOnce &uio) {
|
|
pthread_mutex_lock(&initMutex);
|
|
int32_t state = uio.fState;
|
|
if (state == 0) {
|
|
umtx_storeRelease(uio.fState, 1);
|
|
pthread_mutex_unlock(&initMutex);
|
|
return TRUE; // Caller will next call the init function.
|
|
} else {
|
|
while (uio.fState == 1) {
|
|
// Another thread is currently running the initialization.
|
|
// Wait until it completes.
|
|
pthread_cond_wait(&initCondition, &initMutex);
|
|
}
|
|
pthread_mutex_unlock(&initMutex);
|
|
U_ASSERT(uio.fState == 2);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// This function is called by the thread that ran an initialization function,
|
|
// just after completing the function.
|
|
// Some threads may be waiting on the condition, requiring the broadcast wakeup.
|
|
// Some threads may be racing to test the fState variable outside of the mutex,
|
|
// requiring the use of store/release when changing its value.
|
|
|
|
U_COMMON_API void U_EXPORT2
|
|
umtx_initImplPostInit(UInitOnce &uio) {
|
|
pthread_mutex_lock(&initMutex);
|
|
umtx_storeRelease(uio.fState, 2);
|
|
pthread_cond_broadcast(&initCondition);
|
|
pthread_mutex_unlock(&initMutex);
|
|
}
|
|
|
|
U_NAMESPACE_END
|
|
|
|
// End of POSIX specific umutex implementation.
|
|
|
|
#else // Platform #define chain.
|
|
|
|
#error Unknown Platform
|
|
|
|
#endif // Platform #define chain.
|
|
|
|
|
|
//-------------------------------------------------------------------------------
|
|
//
|
|
// Atomic Operations, out-of-line versions.
|
|
// These are conditional, only defined if better versions
|
|
// were not available for the platform.
|
|
//
|
|
// These versions are platform neutral.
|
|
//
|
|
//--------------------------------------------------------------------------------
|
|
|
|
#if defined U_NO_PLATFORM_ATOMICS
|
|
static UMutex gIncDecMutex = U_MUTEX_INITIALIZER;
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
U_COMMON_API int32_t U_EXPORT2
|
|
umtx_atomic_inc(u_atomic_int32_t *p) {
|
|
int32_t retVal;
|
|
umtx_lock(&gIncDecMutex);
|
|
retVal = ++(*p);
|
|
umtx_unlock(&gIncDecMutex);
|
|
return retVal;
|
|
}
|
|
|
|
|
|
U_COMMON_API int32_t U_EXPORT2
|
|
umtx_atomic_dec(u_atomic_int32_t *p) {
|
|
int32_t retVal;
|
|
umtx_lock(&gIncDecMutex);
|
|
retVal = --(*p);
|
|
umtx_unlock(&gIncDecMutex);
|
|
return retVal;
|
|
}
|
|
|
|
U_COMMON_API int32_t U_EXPORT2
|
|
umtx_loadAcquire(u_atomic_int32_t &var) {
|
|
umtx_lock(&gIncDecMutex);
|
|
int32_t val = var;
|
|
umtx_unlock(&gIncDecMutex);
|
|
return val;
|
|
}
|
|
|
|
U_COMMON_API void U_EXPORT2
|
|
umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
|
|
umtx_lock(&gIncDecMutex);
|
|
var = val;
|
|
umtx_unlock(&gIncDecMutex);
|
|
}
|
|
|
|
U_NAMESPACE_END
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Deprecated functions for setting user mutexes.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
U_DEPRECATED void U_EXPORT2
|
|
u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
|
|
UMtxFn *, UMtxFn *, UErrorCode *status) {
|
|
if (U_SUCCESS(*status)) {
|
|
*status = U_UNSUPPORTED_ERROR;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
U_DEPRECATED void U_EXPORT2
|
|
u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
|
|
UErrorCode *status) {
|
|
if (U_SUCCESS(*status)) {
|
|
*status = U_UNSUPPORTED_ERROR;
|
|
}
|
|
return;
|
|
}
|