Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/common/umutex.cpp
Line
Count
Source (jump to first uncovered line)
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
30
U_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
46
namespace {
47
std::mutex *initMutex;
48
std::condition_variable *initCondition;
49
50
// The ICU global mutex.
51
// Used when ICU implementation code passes nullptr for the mutex pointer.
52
UMutex globalMutex;
53
54
std::once_flag initFlag;
55
std::once_flag *pInitFlag = &initFlag;
56
57
}  // Anonymous namespace
58
59
U_CDECL_BEGIN
60
0
static UBool U_CALLCONV umtx_cleanup() {
61
0
    initMutex->~mutex();
62
0
    initCondition->~condition_variable();
63
0
    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
0
    pInitFlag->~once_flag();
68
0
    pInitFlag = new(&initFlag) std::once_flag();
69
0
    return true;
70
0
}
71
72
0
static void U_CALLCONV umtx_init() {
73
0
    initMutex = STATIC_NEW(std::mutex);
74
0
    initCondition = STATIC_NEW(std::condition_variable);
75
0
    ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
76
0
}
77
U_CDECL_END
78
79
80
0
std::mutex *UMutex::getMutex() {
81
0
    std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
82
0
    if (retPtr == nullptr) {
83
0
        std::call_once(*pInitFlag, umtx_init);
84
0
        std::lock_guard<std::mutex> guard(*initMutex);
85
0
        retPtr = fMutex.load(std::memory_order_acquire);
86
0
        if (retPtr == nullptr) {
87
0
            fMutex = new(fStorage) std::mutex();
88
0
            retPtr = fMutex;
89
0
            fListLink = gListHead;
90
0
            gListHead = this;
91
0
        }
92
0
    }
93
0
    U_ASSERT(retPtr != nullptr);
94
0
    return retPtr;
95
0
}
96
97
UMutex *UMutex::gListHead = nullptr;
98
99
0
void UMutex::cleanup() {
100
0
    UMutex *next = nullptr;
101
0
    for (UMutex *m = gListHead; m != nullptr; m = next) {
102
0
        (*m->fMutex).~mutex();
103
0
        m->fMutex = nullptr;
104
0
        next = m->fListLink;
105
0
        m->fListLink = nullptr;
106
0
    }
107
0
    gListHead = nullptr;
108
0
}
109
110
111
U_CAPI void  U_EXPORT2
112
0
umtx_lock(UMutex *mutex) {
113
0
    if (mutex == nullptr) {
114
0
        mutex = &globalMutex;
115
0
    }
116
0
    mutex->lock();
117
0
}
118
119
120
U_CAPI void  U_EXPORT2
121
umtx_unlock(UMutex* mutex)
122
0
{
123
0
    if (mutex == nullptr) {
124
0
        mutex = &globalMutex;
125
0
    }
126
0
    mutex->unlock();
127
0
}
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
//
144
U_COMMON_API UBool U_EXPORT2
145
0
umtx_initImplPreInit(UInitOnce &uio) {
146
0
    std::call_once(*pInitFlag, umtx_init);
147
0
    std::unique_lock<std::mutex> lock(*initMutex);
148
0
    if (umtx_loadAcquire(uio.fState) == 0) {
149
0
        umtx_storeRelease(uio.fState, 1);
150
0
        return true;      // Caller will next call the init function.
151
0
    } else {
152
0
        while (umtx_loadAcquire(uio.fState) == 1) {
153
            // Another thread is currently running the initialization.
154
            // Wait until it completes.
155
0
            initCondition->wait(lock);
156
0
        }
157
0
        U_ASSERT(uio.fState == 2);
158
0
        return false;
159
0
    }
160
0
}
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
169
U_COMMON_API void U_EXPORT2
170
0
umtx_initImplPostInit(UInitOnce &uio) {
171
0
    {
172
0
        std::unique_lock<std::mutex> lock(*initMutex);
173
0
        umtx_storeRelease(uio.fState, 2);
174
0
    }
175
0
    initCondition->notify_all();
176
0
}
177
178
U_NAMESPACE_END
179
180
/*************************************************************************************************
181
 *
182
 *  Deprecated functions for setting user mutexes.
183
 *
184
 *************************************************************************************************/
185
186
U_DEPRECATED void U_EXPORT2
187
u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
188
0
                    UMtxFn *,  UMtxFn *, UErrorCode *status) {
189
0
    if (U_SUCCESS(*status)) {
190
0
        *status = U_UNSUPPORTED_ERROR;
191
0
    }
192
0
    return;
193
0
}
194
195
196
197
U_DEPRECATED void U_EXPORT2
198
u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
199
0
                           UErrorCode *status) {
200
0
    if (U_SUCCESS(*status)) {
201
0
        *status = U_UNSUPPORTED_ERROR;
202
0
    }
203
0
    return;
204
0
}