xref: /third_party/icu/icu4c/source/common/umutex.cpp (revision 2e5b6d6d)
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4******************************************************************************
5*
6*   Copyright (C) 1997-2016, International Business Machines
7*   Corporation and others.  All Rights Reserved.
8*
9******************************************************************************
10*
11* File umutex.cpp
12*
13* Modification History:
14*
15*   Date        Name        Description
16*   04/02/97    aliu        Creation.
17*   04/07/99    srl         updated
18*   05/13/99    stephen     Changed to umutex (from cmutex).
19*   11/22/99    aliu        Make non-global mutex autoinitialize [j151]
20******************************************************************************
21*/
22
23#include "umutex.h"
24
25#include "unicode/utypes.h"
26#include "uassert.h"
27#include "ucln_cmn.h"
28#include "cmemory.h"
29
30U_NAMESPACE_BEGIN
31
32
33#if defined(U_USER_MUTEX_CPP)
34// Support for including an alternate implementation of mutexes has been withdrawn.
35// See issue ICU-20185.
36#error U_USER_MUTEX_CPP not supported
37#endif
38
39
40/*************************************************************************************************
41 *
42 *  ICU Mutex wrappers.
43 *
44 *************************************************************************************************/
45
46namespace {
47std::mutex *initMutex;
48std::condition_variable *initCondition;
49
50// The ICU global mutex.
51// Used when ICU implementation code passes nullptr for the mutex pointer.
52UMutex globalMutex;
53
54std::once_flag initFlag;
55std::once_flag *pInitFlag = &initFlag;
56
57}  // Anonymous namespace
58
59U_CDECL_BEGIN
60static UBool U_CALLCONV umtx_cleanup() {
61    initMutex->~mutex();
62    initCondition->~condition_variable();
63    UMutex::cleanup();
64
65    // Reset the once_flag, by destructing it and creating a fresh one in its place.
66    // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once().
67    pInitFlag->~once_flag();
68    pInitFlag = new(&initFlag) std::once_flag();
69    return true;
70}
71
72static void U_CALLCONV umtx_init() {
73    initMutex = STATIC_NEW(std::mutex);
74    initCondition = STATIC_NEW(std::condition_variable);
75    ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
76}
77U_CDECL_END
78
79
80std::mutex *UMutex::getMutex() {
81    std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
82    if (retPtr == nullptr) {
83        std::call_once(*pInitFlag, umtx_init);
84        std::lock_guard<std::mutex> guard(*initMutex);
85        retPtr = fMutex.load(std::memory_order_acquire);
86        if (retPtr == nullptr) {
87            fMutex = new(fStorage) std::mutex();
88            retPtr = fMutex;
89            fListLink = gListHead;
90            gListHead = this;
91        }
92    }
93    U_ASSERT(retPtr != nullptr);
94    return retPtr;
95}
96
97UMutex *UMutex::gListHead = nullptr;
98
99void UMutex::cleanup() {
100    UMutex *next = nullptr;
101    for (UMutex *m = gListHead; m != nullptr; m = next) {
102        (*m->fMutex).~mutex();
103        m->fMutex = nullptr;
104        next = m->fListLink;
105        m->fListLink = nullptr;
106    }
107    gListHead = nullptr;
108}
109
110
111U_CAPI void  U_EXPORT2
112umtx_lock(UMutex *mutex) {
113    if (mutex == nullptr) {
114        mutex = &globalMutex;
115    }
116    mutex->lock();
117}
118
119
120U_CAPI void  U_EXPORT2
121umtx_unlock(UMutex* mutex)
122{
123    if (mutex == nullptr) {
124        mutex = &globalMutex;
125    }
126    mutex->unlock();
127}
128
129
130/*************************************************************************************************
131 *
132 *  UInitOnce Implementation
133 *
134 *************************************************************************************************/
135
136// This function is called when a test of a UInitOnce::fState reveals that
137//   initialization has not completed, that we either need to call the init
138//   function on this thread, or wait for some other thread to complete.
139//
140// The actual call to the init function is made inline by template code
141//   that knows the C++ types involved. This function returns true if
142//   the caller needs to call the Init function.
143//
144U_COMMON_API UBool U_EXPORT2
145umtx_initImplPreInit(UInitOnce &uio) {
146    std::call_once(*pInitFlag, umtx_init);
147    std::unique_lock<std::mutex> lock(*initMutex);
148    if (umtx_loadAcquire(uio.fState) == 0) {
149        umtx_storeRelease(uio.fState, 1);
150        return true;      // Caller will next call the init function.
151    } else {
152        while (umtx_loadAcquire(uio.fState) == 1) {
153            // Another thread is currently running the initialization.
154            // Wait until it completes.
155            initCondition->wait(lock);
156        }
157        U_ASSERT(uio.fState == 2);
158        return false;
159    }
160}
161
162
163// This function is called by the thread that ran an initialization function,
164// just after completing the function.
165//   Some threads may be waiting on the condition, requiring the broadcast wakeup.
166//   Some threads may be racing to test the fState variable outside of the mutex,
167//   requiring the use of store/release when changing its value.
168
169U_COMMON_API void U_EXPORT2
170umtx_initImplPostInit(UInitOnce &uio) {
171    {
172        std::unique_lock<std::mutex> lock(*initMutex);
173        umtx_storeRelease(uio.fState, 2);
174    }
175    initCondition->notify_all();
176}
177
178U_NAMESPACE_END
179
180/*************************************************************************************************
181 *
182 *  Deprecated functions for setting user mutexes.
183 *
184 *************************************************************************************************/
185
186U_DEPRECATED void U_EXPORT2
187u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
188                    UMtxFn *,  UMtxFn *, UErrorCode *status) {
189    if (U_SUCCESS(*status)) {
190        *status = U_UNSUPPORTED_ERROR;
191    }
192    return;
193}
194
195
196
197U_DEPRECATED void U_EXPORT2
198u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
199                           UErrorCode *status) {
200    if (U_SUCCESS(*status)) {
201        *status = U_UNSUPPORTED_ERROR;
202    }
203    return;
204}
205