forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			224 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			224 lines
		
	
	
	
		
			6.1 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 "ucln_cmn.h"
 | 
						|
#include "cmemory.h"
 | 
						|
 | 
						|
U_NAMESPACE_BEGIN
 | 
						|
 | 
						|
 | 
						|
#if defined(U_USER_MUTEX_CPP)
 | 
						|
// Support for including an alternate implementation of mutexes has been withdrawn.
 | 
						|
// See issue ICU-20185.
 | 
						|
#error U_USER_MUTEX_CPP not supported
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/*************************************************************************************************
 | 
						|
 *
 | 
						|
 *  ICU Mutex wrappers.
 | 
						|
 *
 | 
						|
 *************************************************************************************************/
 | 
						|
 | 
						|
#ifndef __wasi__
 | 
						|
namespace {
 | 
						|
std::mutex *initMutex;
 | 
						|
std::condition_variable *initCondition;
 | 
						|
 | 
						|
// The ICU global mutex.
 | 
						|
// Used when ICU implementation code passes nullptr for the mutex pointer.
 | 
						|
UMutex globalMutex;
 | 
						|
 | 
						|
std::once_flag initFlag;
 | 
						|
std::once_flag *pInitFlag = &initFlag;
 | 
						|
 | 
						|
}  // Anonymous namespace
 | 
						|
#endif
 | 
						|
 | 
						|
U_CDECL_BEGIN
 | 
						|
static UBool U_CALLCONV umtx_cleanup() {
 | 
						|
#ifndef __wasi__
 | 
						|
    initMutex->~mutex();
 | 
						|
    initCondition->~condition_variable();
 | 
						|
    UMutex::cleanup();
 | 
						|
 | 
						|
    // Reset the once_flag, by destructing it and creating a fresh one in its place.
 | 
						|
    // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once().
 | 
						|
    pInitFlag->~once_flag();
 | 
						|
    pInitFlag = new(&initFlag) std::once_flag();
 | 
						|
#endif
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static void U_CALLCONV umtx_init() {
 | 
						|
#ifndef __wasi__
 | 
						|
    initMutex = STATIC_NEW(std::mutex);
 | 
						|
    initCondition = STATIC_NEW(std::condition_variable);
 | 
						|
    ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
 | 
						|
#endif
 | 
						|
}
 | 
						|
U_CDECL_END
 | 
						|
 | 
						|
 | 
						|
#ifndef __wasi__
 | 
						|
std::mutex *UMutex::getMutex() {
 | 
						|
    std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
 | 
						|
    if (retPtr == nullptr) {
 | 
						|
        std::call_once(*pInitFlag, umtx_init);
 | 
						|
        std::lock_guard<std::mutex> guard(*initMutex);
 | 
						|
        retPtr = fMutex.load(std::memory_order_acquire);
 | 
						|
        if (retPtr == nullptr) {
 | 
						|
            fMutex = new(fStorage) std::mutex();
 | 
						|
            retPtr = fMutex;
 | 
						|
            fListLink = gListHead;
 | 
						|
            gListHead = this;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    U_ASSERT(retPtr != nullptr);
 | 
						|
    return retPtr;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
UMutex *UMutex::gListHead = nullptr;
 | 
						|
 | 
						|
void UMutex::cleanup() {
 | 
						|
    UMutex *next = nullptr;
 | 
						|
    for (UMutex *m = gListHead; m != nullptr; m = next) {
 | 
						|
#ifndef __wasi__
 | 
						|
        (*m->fMutex).~mutex();
 | 
						|
        m->fMutex = nullptr;
 | 
						|
#endif
 | 
						|
        next = m->fListLink;
 | 
						|
        m->fListLink = nullptr;
 | 
						|
    }
 | 
						|
    gListHead = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
U_CAPI void  U_EXPORT2
 | 
						|
umtx_lock(UMutex *mutex) {
 | 
						|
#ifndef __wasi__
 | 
						|
    if (mutex == nullptr) {
 | 
						|
        mutex = &globalMutex;
 | 
						|
    }
 | 
						|
    mutex->lock();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
U_CAPI void  U_EXPORT2
 | 
						|
umtx_unlock(UMutex* mutex)
 | 
						|
{
 | 
						|
#ifndef __wasi__
 | 
						|
    if (mutex == nullptr) {
 | 
						|
        mutex = &globalMutex;
 | 
						|
    }
 | 
						|
    mutex->unlock();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*************************************************************************************************
 | 
						|
 *
 | 
						|
 *  UInitOnce Implementation
 | 
						|
 *
 | 
						|
 *************************************************************************************************/
 | 
						|
 | 
						|
// This function is called when a test of a UInitOnce::fState reveals that
 | 
						|
//   initialization has not completed, that we either need to call the init
 | 
						|
//   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) {
 | 
						|
#ifndef __wasi__
 | 
						|
    std::call_once(*pInitFlag, umtx_init);
 | 
						|
    std::unique_lock<std::mutex> lock(*initMutex);
 | 
						|
#endif
 | 
						|
    if (umtx_loadAcquire(uio.fState) == 0) {
 | 
						|
        umtx_storeRelease(uio.fState, 1);
 | 
						|
        return true;      // Caller will next call the init function.
 | 
						|
    } else {
 | 
						|
#ifndef __wasi__
 | 
						|
        while (umtx_loadAcquire(uio.fState) == 1) {
 | 
						|
            // Another thread is currently running the initialization.
 | 
						|
            // Wait until it completes.
 | 
						|
            initCondition->wait(lock);
 | 
						|
        }
 | 
						|
        U_ASSERT(uio.fState == 2);
 | 
						|
#endif
 | 
						|
        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) {
 | 
						|
#ifndef __wasi__
 | 
						|
    {
 | 
						|
        std::unique_lock<std::mutex> lock(*initMutex);
 | 
						|
        umtx_storeRelease(uio.fState, 2);
 | 
						|
    }
 | 
						|
    initCondition->notify_all();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
U_NAMESPACE_END
 | 
						|
 | 
						|
/*************************************************************************************************
 | 
						|
 *
 | 
						|
 *  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;
 | 
						|
}
 |