Coverage Report

Created: 2023-06-07 07:17

/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 "cmemory.h"
28
29
30
// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
31
static UMutex   globalMutex = U_MUTEX_INITIALIZER;
32
33
/*
34
 * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
35
 * platform independent set of mutex operations.  For internal ICU use only.
36
 */
37
38
#if defined(U_USER_MUTEX_CPP)
39
// Build time user mutex hook: #include "U_USER_MUTEX_CPP"
40
#include U_MUTEX_XSTR(U_USER_MUTEX_CPP)
41
42
#elif U_PLATFORM_USES_ONLY_WIN32_API
43
44
#if defined U_NO_PLATFORM_ATOMICS
45
#error ICU on Win32 requires support for low level atomic operations.
46
// Visual Studio, gcc, clang are OK. Shouldn't get here.
47
#endif
48
49
50
// This function is called when a test of a UInitOnce::fState reveals that
51
//   initialization has not completed, that we either need to call the
52
//   function on this thread, or wait for some other thread to complete.
53
//
54
// The actual call to the init function is made inline by template code
55
//   that knows the C++ types involved. This function returns TRUE if
56
//   the caller needs to call the Init function.
57
//
58
59
U_NAMESPACE_BEGIN
60
61
U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
62
    for (;;) {
63
        int32_t previousState = InterlockedCompareExchange(
64
            (LONG volatile *) // this is the type given in the API doc for this function.
65
                &uio.fState,  //  Destination
66
            1,            //  Exchange Value
67
            0);           //  Compare value
68
69
        if (previousState == 0) {
70
            return true;   // Caller will next call the init function.
71
                           // Current state == 1.
72
        } else if (previousState == 2) {
73
            // Another thread already completed the initialization.
74
            //   We can simply return FALSE, indicating no
75
            //   further action is needed by the caller.
76
            return FALSE;
77
        } else {
78
            // Another thread is currently running the initialization.
79
            // Wait until it completes.
80
            do {
81
                Sleep(1);
82
                previousState = umtx_loadAcquire(uio.fState);
83
            } while (previousState == 1);
84
        }
85
    }
86
}
87
88
// This function is called by the thread that ran an initialization function,
89
// just after completing the function.
90
91
U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
92
    umtx_storeRelease(uio.fState, 2);
93
}
94
95
U_NAMESPACE_END
96
97
static void winMutexInit(CRITICAL_SECTION *cs) {
98
    InitializeCriticalSection(cs);
99
    return;
100
}
101
102
U_CAPI void  U_EXPORT2
103
umtx_lock(UMutex *mutex) {
104
    if (mutex == NULL) {
105
        mutex = &globalMutex;
106
    }
107
    CRITICAL_SECTION *cs = &mutex->fCS;
108
    umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
109
    EnterCriticalSection(cs);
110
}
111
112
U_CAPI void  U_EXPORT2
113
umtx_unlock(UMutex* mutex)
114
{
115
    if (mutex == NULL) {
116
        mutex = &globalMutex;
117
    }
118
    LeaveCriticalSection(&mutex->fCS);
119
}
120
121
122
U_CAPI void U_EXPORT2
123
umtx_condBroadcast(UConditionVar *condition) {
124
    // We require that the associated mutex be held by the caller,
125
    //  so access to fWaitCount is protected and safe. No other thread can
126
    //  call condWait() while we are here.
127
    if (condition->fWaitCount == 0) {
128
        return;
129
    }
130
    ResetEvent(condition->fExitGate);
131
    SetEvent(condition->fEntryGate);
132
}
133
134
U_CAPI void U_EXPORT2
135
umtx_condSignal(UConditionVar *condition) {
136
    // Function not implemented. There is no immediate requirement from ICU to have it.
137
    // Once ICU drops support for Windows XP and Server 2003, ICU Condition Variables will be
138
    // changed to be thin wrappers on native Windows CONDITION_VARIABLEs, and this function
139
    // becomes trivial to provide.
140
    U_ASSERT(FALSE);
141
}
142
143
U_CAPI void U_EXPORT2
144
umtx_condWait(UConditionVar *condition, UMutex *mutex) {
145
    if (condition->fEntryGate == NULL) {
146
        // Note: because the associated mutex must be locked when calling
147
        //       wait, we know that there can not be multiple threads
148
        //       running here with the same condition variable.
149
        //       Meaning that lazy initialization is safe.
150
        U_ASSERT(condition->fExitGate == NULL);
151
        condition->fEntryGate = CreateEvent(NULL,   // Security Attributes
152
                                            TRUE,   // Manual Reset
153
                                            FALSE,  // Initially reset
154
                                            NULL);  // Name.
155
        U_ASSERT(condition->fEntryGate != NULL);
156
        condition->fExitGate = CreateEvent(NULL, TRUE, TRUE, NULL);
157
        U_ASSERT(condition->fExitGate != NULL);
158
    }
159
160
    condition->fWaitCount++;
161
    umtx_unlock(mutex);
162
    WaitForSingleObject(condition->fEntryGate, INFINITE); 
163
    umtx_lock(mutex);
164
    condition->fWaitCount--;
165
    if (condition->fWaitCount == 0) {
166
        // All threads that were waiting at the entry gate have woken up
167
        // and moved through. Shut the entry gate and open the exit gate.
168
        ResetEvent(condition->fEntryGate);
169
        SetEvent(condition->fExitGate);
170
    } else {
171
        umtx_unlock(mutex);
172
        WaitForSingleObject(condition->fExitGate, INFINITE);
173
        umtx_lock(mutex);
174
    }
175
}
176
177
178
#elif U_PLATFORM_IMPLEMENTS_POSIX
179
180
//-------------------------------------------------------------------------------------------
181
//
182
//  POSIX specific definitions
183
//
184
//-------------------------------------------------------------------------------------------
185
186
# include <pthread.h>
187
188
// Each UMutex consists of a pthread_mutex_t.
189
// All are statically initialized and ready for use.
190
// There is no runtime mutex initialization code needed.
191
192
U_CAPI void  U_EXPORT2
193
20.4k
umtx_lock(UMutex *mutex) {
194
20.4k
    if (mutex == NULL) {
195
13.3k
        mutex = &globalMutex;
196
13.3k
    }
197
20.4k
    int sysErr = pthread_mutex_lock(&mutex->fMutex);
198
20.4k
    (void)sysErr;   // Suppress unused variable warnings.
199
20.4k
    U_ASSERT(sysErr == 0);
200
20.4k
}
201
202
203
U_CAPI void  U_EXPORT2
204
umtx_unlock(UMutex* mutex)
205
20.4k
{
206
20.4k
    if (mutex == NULL) {
207
13.3k
        mutex = &globalMutex;
208
13.3k
    }
209
20.4k
    int sysErr = pthread_mutex_unlock(&mutex->fMutex);
210
20.4k
    (void)sysErr;   // Suppress unused variable warnings.
211
20.4k
    U_ASSERT(sysErr == 0);
212
20.4k
}
213
214
215
U_CAPI void U_EXPORT2
216
0
umtx_condWait(UConditionVar *cond, UMutex *mutex) {
217
0
    if (mutex == NULL) {
218
0
        mutex = &globalMutex;
219
0
    }
220
0
    int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex);
221
0
    (void)sysErr;
222
0
    U_ASSERT(sysErr == 0);
223
0
}
224
225
U_CAPI void U_EXPORT2
226
0
umtx_condBroadcast(UConditionVar *cond) {
227
0
    int sysErr = pthread_cond_broadcast(&cond->fCondition);
228
0
    (void)sysErr;
229
0
    U_ASSERT(sysErr == 0);
230
0
}
231
232
U_CAPI void U_EXPORT2
233
0
umtx_condSignal(UConditionVar *cond) {
234
0
    int sysErr = pthread_cond_signal(&cond->fCondition);
235
0
    (void)sysErr;
236
0
    U_ASSERT(sysErr == 0);
237
0
}
238
239
240
241
U_NAMESPACE_BEGIN
242
243
static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
244
static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
245
246
247
// This function is called when a test of a UInitOnce::fState reveals that
248
//   initialization has not completed, that we either need to call the
249
//   function on this thread, or wait for some other thread to complete.
250
//
251
// The actual call to the init function is made inline by template code
252
//   that knows the C++ types involved. This function returns TRUE if
253
//   the caller needs to call the Init function.
254
//
255
U_COMMON_API UBool U_EXPORT2
256
2.38k
umtx_initImplPreInit(UInitOnce &uio) {
257
2.38k
    pthread_mutex_lock(&initMutex);
258
2.38k
    int32_t state = uio.fState;
259
2.38k
    if (state == 0) {
260
2.38k
        umtx_storeRelease(uio.fState, 1);
261
2.38k
        pthread_mutex_unlock(&initMutex);
262
2.38k
        return TRUE;   // Caller will next call the init function.
263
2.38k
    } else {
264
0
        while (uio.fState == 1) {
265
            // Another thread is currently running the initialization.
266
            // Wait until it completes.
267
0
            pthread_cond_wait(&initCondition, &initMutex);
268
0
        }
269
0
        pthread_mutex_unlock(&initMutex);
270
0
        U_ASSERT(uio.fState == 2);
271
0
        return FALSE;
272
0
    }
273
2.38k
}
274
275
276
277
// This function is called by the thread that ran an initialization function,
278
// just after completing the function.
279
//   Some threads may be waiting on the condition, requiring the broadcast wakeup.
280
//   Some threads may be racing to test the fState variable outside of the mutex,
281
//   requiring the use of store/release when changing its value.
282
283
U_COMMON_API void U_EXPORT2
284
2.38k
umtx_initImplPostInit(UInitOnce &uio) {
285
2.38k
    pthread_mutex_lock(&initMutex);
286
2.38k
    umtx_storeRelease(uio.fState, 2);
287
2.38k
    pthread_cond_broadcast(&initCondition);
288
2.38k
    pthread_mutex_unlock(&initMutex);
289
2.38k
}
290
291
U_NAMESPACE_END
292
293
// End of POSIX specific umutex implementation.
294
295
#else  // Platform #define chain.
296
297
#error Unknown Platform
298
299
#endif  // Platform #define chain.
300
301
302
//-------------------------------------------------------------------------------
303
//
304
//   Atomic Operations, out-of-line versions.
305
//                      These are conditional, only defined if better versions
306
//                      were not available for the platform.
307
//
308
//                      These versions are platform neutral.
309
//
310
//--------------------------------------------------------------------------------
311
312
#if defined U_NO_PLATFORM_ATOMICS
313
static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
314
315
U_NAMESPACE_BEGIN
316
317
U_COMMON_API int32_t U_EXPORT2
318
umtx_atomic_inc(u_atomic_int32_t *p)  {
319
    int32_t retVal;
320
    umtx_lock(&gIncDecMutex);
321
    retVal = ++(*p);
322
    umtx_unlock(&gIncDecMutex);
323
    return retVal;
324
}
325
326
327
U_COMMON_API int32_t U_EXPORT2
328
umtx_atomic_dec(u_atomic_int32_t *p) {
329
    int32_t retVal;
330
    umtx_lock(&gIncDecMutex);
331
    retVal = --(*p);
332
    umtx_unlock(&gIncDecMutex);
333
    return retVal;
334
}
335
336
U_COMMON_API int32_t U_EXPORT2
337
umtx_loadAcquire(u_atomic_int32_t &var) {
338
    umtx_lock(&gIncDecMutex);
339
    int32_t val = var;
340
    umtx_unlock(&gIncDecMutex);
341
    return val;
342
}
343
344
U_COMMON_API void U_EXPORT2
345
umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
346
    umtx_lock(&gIncDecMutex);
347
    var = val;
348
    umtx_unlock(&gIncDecMutex);
349
}
350
351
U_NAMESPACE_END
352
#endif
353
354
//--------------------------------------------------------------------------
355
//
356
//  Deprecated functions for setting user mutexes.
357
//
358
//--------------------------------------------------------------------------
359
360
U_DEPRECATED void U_EXPORT2
361
u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,
362
0
                    UMtxFn *,  UMtxFn *, UErrorCode *status) {
363
0
    if (U_SUCCESS(*status)) {
364
0
        *status = U_UNSUPPORTED_ERROR;
365
0
    }
366
0
    return;
367
0
}
368
369
370
371
U_DEPRECATED void U_EXPORT2
372
u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *,
373
0
                           UErrorCode *status) {
374
0
    if (U_SUCCESS(*status)) {
375
0
        *status = U_UNSUPPORTED_ERROR;
376
0
    }
377
0
    return;
378
0
}