12e5b6d6dSopenharmony_ci--- 22e5b6d6dSopenharmony_cilayout: default 32e5b6d6dSopenharmony_cititle: Custom ICU4C Synchronization 42e5b6d6dSopenharmony_cinav_order: 3 52e5b6d6dSopenharmony_ciparent: Contributors 62e5b6d6dSopenharmony_ci--- 72e5b6d6dSopenharmony_ci<!-- 82e5b6d6dSopenharmony_ci© 2020 and later: Unicode, Inc. and others. 92e5b6d6dSopenharmony_ciLicense & terms of use: http://www.unicode.org/copyright.html 102e5b6d6dSopenharmony_ci--> 112e5b6d6dSopenharmony_ci 122e5b6d6dSopenharmony_ci# Custom ICU4C Synchronization 132e5b6d6dSopenharmony_ci{: .no_toc } 142e5b6d6dSopenharmony_ci 152e5b6d6dSopenharmony_ci## Contents 162e5b6d6dSopenharmony_ci{: .no_toc .text-delta } 172e5b6d6dSopenharmony_ci 182e5b6d6dSopenharmony_ci1. TOC 192e5b6d6dSopenharmony_ci{:toc} 202e5b6d6dSopenharmony_ci 212e5b6d6dSopenharmony_ci--- 222e5b6d6dSopenharmony_ci 232e5b6d6dSopenharmony_ci> :warning: ***Support for including an alternate implementation of atomic and mutex 242e5b6d6dSopenharmony_ci> operations has been withdrawn and removed from ICU4C.*** 252e5b6d6dSopenharmony_ci> See issue [ICU-20185](https://unicode-org.atlassian.net/browse/ICU-20185). 262e5b6d6dSopenharmony_ci 272e5b6d6dSopenharmony_ci### Build Time User Provided Synchronization 282e5b6d6dSopenharmony_ci 292e5b6d6dSopenharmony_ciBuild time user synchronization provides a mechanism for platforms with special 302e5b6d6dSopenharmony_cirequirements to provide their own mutex and one-time initialization 312e5b6d6dSopenharmony_ciimplementations to ICU. This facility was introduced in ICU 53. It may change 322e5b6d6dSopenharmony_ciover time. 332e5b6d6dSopenharmony_ci 342e5b6d6dSopenharmony_ciThe alternative implementations are compiled directly into the ICU libraries. 352e5b6d6dSopenharmony_ciAlternative implementations cannot be plugged in at run time. 362e5b6d6dSopenharmony_ci 372e5b6d6dSopenharmony_ciThe tables below show the items that must be defined by a custom ICU 382e5b6d6dSopenharmony_cisynchronization implementation. The list includes both functions that are used 392e5b6d6dSopenharmony_cithroughout ICU code and additional functions are for internal by other ICU 402e5b6d6dSopenharmony_cisynchronization primitives. 412e5b6d6dSopenharmony_ci 422e5b6d6dSopenharmony_ci**Low Level Atomics**, a set of platform or compiler dependent typedefs and 432e5b6d6dSopenharmony_ciinlines. Provided in the internal header file 442e5b6d6dSopenharmony_ci[`umutex.h`](https://github.com/unicode-org/icu/blob/main/icu4c/source/common/umutex.h). 452e5b6d6dSopenharmony_ci 462e5b6d6dSopenharmony_ci| Type/Function | Description | 472e5b6d6dSopenharmony_ci|---------------------------------------------------------|-------------------------------------------------------------------------------| 482e5b6d6dSopenharmony_ci| `typedef u_atomic_int32_t` | A 32 bit integer that will work with low level atomic operations. (`typedef`) | 492e5b6d6dSopenharmony_ci| `umtx_loadAcquire(u_atomic_int32_t &var)` | | 502e5b6d6dSopenharmony_ci| `umtx_storeRelease(u_atomic_int32_t &var, int32_t val)` | | 512e5b6d6dSopenharmony_ci| `umtx_atomic_inc(u_atomic_int32_t &var)` | | 522e5b6d6dSopenharmony_ci| `umtx_atomic_dec(u_atomic_int32_t &var)` | | 532e5b6d6dSopenharmony_ci 542e5b6d6dSopenharmony_ci**Mutexes**. Type declarations for ICU mutex wrappers. Provided in a header file. 552e5b6d6dSopenharmony_ci 562e5b6d6dSopenharmony_ci| Type | Description | 572e5b6d6dSopenharmony_ci|-----------------------|---------------------------------------------------------------------------------------------------| 582e5b6d6dSopenharmony_ci| `struct UMutex` | An ICU mutex. All instances will be static. Typically just contains an underlying platform mutex. | 592e5b6d6dSopenharmony_ci| `U_MUTEX_INITIALIZER` | A C style initializer for a static instance of a `UMutex`. | 602e5b6d6dSopenharmony_ci 612e5b6d6dSopenharmony_ci**Mutex and InitOnce implementations**. Out-of-line platform-specific code. 622e5b6d6dSopenharmony_ciProvided in a .cpp file. 632e5b6d6dSopenharmony_ci 642e5b6d6dSopenharmony_ci| Function | Description | 652e5b6d6dSopenharmony_ci|-----------------------------------------|--------------------------------------------| 662e5b6d6dSopenharmony_ci| `umtx_lock(UMutex *mutex)` | Lock a mutex. | 672e5b6d6dSopenharmony_ci| `umtx_unlock(UMutex* mutex)` | Unlock a mutex. | 682e5b6d6dSopenharmony_ci| `umtx_initImplPreInit(UInitOnce &uio)` | `umtx_initOnce()` implementation function. | 692e5b6d6dSopenharmony_ci| `umtx_initImplPostInit(UInitOnce &uio)` | `umtx_initOnce()` implementation function. | 702e5b6d6dSopenharmony_ci 712e5b6d6dSopenharmony_ci`UInitOnce` and `umtx_initOnce()` are used internally by ICU for thread-safe 722e5b6d6dSopenharmony_cione-time initialization. Their implementation is split into a 732e5b6d6dSopenharmony_ciplatform-independent part (contained in 742e5b6d6dSopenharmony_ci[`umutex.h`](https://github.com/unicode-org/icu/blob/main/icu4c/source/common/umutex.h)), 752e5b6d6dSopenharmony_ciand the pair of platform-dependent implementation functions listed above. 762e5b6d6dSopenharmony_ci 772e5b6d6dSopenharmony_ci**Build Setup** 782e5b6d6dSopenharmony_ci 792e5b6d6dSopenharmony_ciCompiler preprocessor variables are used to name the custom files to be included 802e5b6d6dSopenharmony_ciin the ICU build. If defined, the files are included at the top of the normal 812e5b6d6dSopenharmony_ciplatform `#ifdef` chains in the ICU sources, and effectively define a new 822e5b6d6dSopenharmony_ciplatform. 832e5b6d6dSopenharmony_ci 842e5b6d6dSopenharmony_ci| Macro | Description | 852e5b6d6dSopenharmony_ci|--------------------|---------------------------------------------------------| 862e5b6d6dSopenharmony_ci| `U_USER_ATOMICS_H` | Set to the name of the low level atomics header file. | 872e5b6d6dSopenharmony_ci| `U_USER_MUTEX_H` | Mutexes header file. | 882e5b6d6dSopenharmony_ci| `U_USER_MUTEX_CPP` | Mutexes and `InitOnce` implementation file. | 892e5b6d6dSopenharmony_ci 902e5b6d6dSopenharmony_ciIt is possible (and reasonable) to supply only the two mutex files, while 912e5b6d6dSopenharmony_ciretaining the ICU default implementation for the low level atomics. 922e5b6d6dSopenharmony_ci 932e5b6d6dSopenharmony_ciExample ICU configure with user mutexes specified: 942e5b6d6dSopenharmony_ci 952e5b6d6dSopenharmony_ci CPPFLAGS='-DU_USER_ATOMICS_H=atomic_c11.h -DU_USER_MUTEX_H=mutex_c11.h -DU_USER_MUTEX_CPP=mutex_c11.cpp' ./runConfigureICU --enable-debug Linux 962e5b6d6dSopenharmony_ci 972e5b6d6dSopenharmony_ci**Stability** 982e5b6d6dSopenharmony_ci 992e5b6d6dSopenharmony_ciThis interface may change between ICU releases. The required set of functions 1002e5b6d6dSopenharmony_cimay be extended, or details of the behavior required may be altered. 1012e5b6d6dSopenharmony_ci 1022e5b6d6dSopenharmony_ciThe types and functions defined by this interface reach deeply into the ICU 1032e5b6d6dSopenharmony_ciimplementation, and we need to retain the ability to make changes should the 1042e5b6d6dSopenharmony_cineed arise. 1052e5b6d6dSopenharmony_ci 1062e5b6d6dSopenharmony_ci**Examples** 1072e5b6d6dSopenharmony_ci 1082e5b6d6dSopenharmony_ciThe code below shows a complete set of ICU user synchronization files. 1092e5b6d6dSopenharmony_ci 1102e5b6d6dSopenharmony_ciThis implementation uses C++11 language mutexes and atomics. These make for a 1112e5b6d6dSopenharmony_ciconvenient reference implementation because the C++11 constructs are well 1122e5b6d6dSopenharmony_cidefined and straight forward to use. 1132e5b6d6dSopenharmony_ci 1142e5b6d6dSopenharmony_ciSimilar implementations for POSIX and Windows can be found in files 1152e5b6d6dSopenharmony_ci`common/umutex.h` and `common/umutex.cpp`, in the platform `#ifdef` chains; these are 1162e5b6d6dSopenharmony_cipart of the standard ICU distribution. 1172e5b6d6dSopenharmony_ci 1182e5b6d6dSopenharmony_ci**Mutex Header** 1192e5b6d6dSopenharmony_ci```c++ 1202e5b6d6dSopenharmony_ci// Example of an ICU build time customized mutex header. 1212e5b6d6dSopenharmony_ci// 1222e5b6d6dSopenharmony_ci// Must define struct UMutex and an initializer that will work with static instances. 1232e5b6d6dSopenharmony_ci// All UMutex instances in ICU code will be static. 1242e5b6d6dSopenharmony_ci 1252e5b6d6dSopenharmony_ci#ifndef ICU_MUTEX_C11_H 1262e5b6d6dSopenharmony_ci#define ICU_MUTEX_C11_H 1272e5b6d6dSopenharmony_ci#include <mutex> 1282e5b6d6dSopenharmony_ci#include <condition_variable> 1292e5b6d6dSopenharmony_cistruct UMutex { 1302e5b6d6dSopenharmony_ci std::mutex fMutex; 1312e5b6d6dSopenharmony_ci}; 1322e5b6d6dSopenharmony_ci#define U_MUTEX_INITIALIZER {} 1332e5b6d6dSopenharmony_ci#endif 1342e5b6d6dSopenharmony_ci``` 1352e5b6d6dSopenharmony_ci 1362e5b6d6dSopenharmony_ci**Atomics Header** 1372e5b6d6dSopenharmony_ci```c++ 1382e5b6d6dSopenharmony_ci#include <atomic> 1392e5b6d6dSopenharmony_ci 1402e5b6d6dSopenharmony_citypedef std::atomic<int32_t> u_atomic_int32_t; 1412e5b6d6dSopenharmony_ci#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) 1422e5b6d6dSopenharmony_ci 1432e5b6d6dSopenharmony_ciinline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 1442e5b6d6dSopenharmony_ci return var.load(std::memory_order_acquire); 1452e5b6d6dSopenharmony_ci} 1462e5b6d6dSopenharmony_ci 1472e5b6d6dSopenharmony_ciinline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 1482e5b6d6dSopenharmony_ci var.store(val, std::memory_order_release); 1492e5b6d6dSopenharmony_ci} 1502e5b6d6dSopenharmony_ci 1512e5b6d6dSopenharmony_ciinline int32_t umtx_atomic_inc(u_atomic_int32_t &var) { 1522e5b6d6dSopenharmony_ci return var.fetch_add(1) + 1; 1532e5b6d6dSopenharmony_ci} 1542e5b6d6dSopenharmony_ci 1552e5b6d6dSopenharmony_ciinline int32_t umtx_atomic_dec(u_atomic_int32_t &var) { 1562e5b6d6dSopenharmony_ci return var.fetch_sub(1) - 1; 1572e5b6d6dSopenharmony_ci} 1582e5b6d6dSopenharmony_ci``` 1592e5b6d6dSopenharmony_ci 1602e5b6d6dSopenharmony_ci**Mutex and InitOnce implementations** 1612e5b6d6dSopenharmony_ci```c++ 1622e5b6d6dSopenharmony_ci// 1632e5b6d6dSopenharmony_ci// Example ICU build time custom mutex cpp file. 1642e5b6d6dSopenharmony_ci// 1652e5b6d6dSopenharmony_ci// Must implement these functions: 1662e5b6d6dSopenharmony_ci// umtx_lock(UMutex *mutex); 1672e5b6d6dSopenharmony_ci// umtx_unlock(UMutex *mutex); 1682e5b6d6dSopenharmony_ci// umtx_initImplPreInit(UInitOnce &uio); 1692e5b6d6dSopenharmony_ci// umtx_initImplPostInit(UInitOnce &uio); 1702e5b6d6dSopenharmony_ci 1712e5b6d6dSopenharmony_ciU_CAPI void U_EXPORT2 1722e5b6d6dSopenharmony_ciumtx_lock(UMutex *mutex) { 1732e5b6d6dSopenharmony_ci if (mutex == NULL) { 1742e5b6d6dSopenharmony_ci // Note: globalMutex is pre-defined in the platform-independent ICU code. 1752e5b6d6dSopenharmony_ci mutex = &globalMutex; 1762e5b6d6dSopenharmony_ci } 1772e5b6d6dSopenharmony_ci mutex->fMutex.lock(); 1782e5b6d6dSopenharmony_ci} 1792e5b6d6dSopenharmony_ci 1802e5b6d6dSopenharmony_ciU_CAPI void U_EXPORT2 1812e5b6d6dSopenharmony_ciumtx_unlock(UMutex* mutex) `{ 1822e5b6d6dSopenharmony_ci if (mutex == NULL) { 1832e5b6d6dSopenharmony_ci mutex = &globalMutex; 1842e5b6d6dSopenharmony_ci } 1852e5b6d6dSopenharmony_ci mutex->fMutex.unlock(); 1862e5b6d6dSopenharmony_ci} 1872e5b6d6dSopenharmony_ci 1882e5b6d6dSopenharmony_ci// A mutex and a condition variable are used by the implementation of umtx_initOnce() 1892e5b6d6dSopenharmony_ci// The mutex is held only while the state of the InitOnce object is being changed or 1902e5b6d6dSopenharmony_ci// tested. It is not held while initialization functions are running. 1912e5b6d6dSopenharmony_ci// Threads needing to block, waiting for an initialization to complete, will wait 1922e5b6d6dSopenharmony_ci// on the condition variable. 1932e5b6d6dSopenharmony_ci// All InitOnce objects share a common mutex and condition variable. This means that 1942e5b6d6dSopenharmony_ci// all blocked threads will wake if any (possibly unrelated) initialization completes. 1952e5b6d6dSopenharmony_ci// Which does no harm, it should be statistically rare, and any spuriously woken 1962e5b6d6dSopenharmony_ci// threads will check their state and promptly wait again. 1972e5b6d6dSopenharmony_ci 1982e5b6d6dSopenharmony_cistatic std::mutex initMutex; 1992e5b6d6dSopenharmony_cistatic std::condition_variable initCondition; 2002e5b6d6dSopenharmony_ci 2012e5b6d6dSopenharmony_ci// This function is called from umtx_initOnce() when an initial test of a UInitOnce::fState flag 2022e5b6d6dSopenharmony_ci// reveals that initialization has not completed, that we either need to call the 2032e5b6d6dSopenharmony_ci// function on this thread, or wait for some other thread to complete the initialization. 2042e5b6d6dSopenharmony_ci// 2052e5b6d6dSopenharmony_ci// The actual call to the init function is made inline by template code 2062e5b6d6dSopenharmony_ci// that knows the C++ types involved. This function returns true if 2072e5b6d6dSopenharmony_ci// the inline code needs to invoke the Init function, or false if the initialization 2082e5b6d6dSopenharmony_ci// has completed on another thread. 2092e5b6d6dSopenharmony_ci// 2102e5b6d6dSopenharmony_ci// UInitOnce::fState values: 2112e5b6d6dSopenharmony_ci// 0: Initialization has not yet begun. 2122e5b6d6dSopenharmony_ci// 1: Initialization is in progress, not yet complete. 2132e5b6d6dSopenharmony_ci// 2: Initialization is complete. 2142e5b6d6dSopenharmony_ci// 2152e5b6d6dSopenharmony_ciUBool umtx_initImplPreInit(UInitOnce &uio) { 2162e5b6d6dSopenharmony_ci std::unique_lock<std::mutex> initLock(initMutex); 2172e5b6d6dSopenharmony_ci int32_t state = uio.fState; 2182e5b6d6dSopenharmony_ci if (state == 0) { 2192e5b6d6dSopenharmony_ci umtx_storeRelease(uio.fState, 1); 2202e5b6d6dSopenharmony_ci return true; // Caller will next call the init function. 2212e5b6d6dSopenharmony_ci } else { 2222e5b6d6dSopenharmony_ci while (uio.fState == 1) { 2232e5b6d6dSopenharmony_ci // Another thread is currently running the initialization. 2242e5b6d6dSopenharmony_ci // Wait until it completes. 2252e5b6d6dSopenharmony_ci initCondition.wait(initLock); 2262e5b6d6dSopenharmony_ci } 2272e5b6d6dSopenharmony_ci U_ASSERT(uio.fState == 2); 2282e5b6d6dSopenharmony_ci return false; 2292e5b6d6dSopenharmony_ci } 2302e5b6d6dSopenharmony_ci} 2312e5b6d6dSopenharmony_ci 2322e5b6d6dSopenharmony_ci// This function is called from umtx_initOnce() just after an initializationfunction completes. 2332e5b6d6dSopenharmony_ci// Its purpose is to set the state of the UInitOnce object to initialized, and to 2342e5b6d6dSopenharmony_ci// unblock any threads that may be waiting on the initialization. 2352e5b6d6dSopenharmony_ci// 2362e5b6d6dSopenharmony_ci// Some threads may be waiting on the condition variable, requiring the notify_all(). 2372e5b6d6dSopenharmony_ci// Some threads may be racing to test the fState flag outside of the mutex, 2382e5b6d6dSopenharmony_ci// requiring the use of store-release when changing its value. 2392e5b6d6dSopenharmony_ci 2402e5b6d6dSopenharmony_civoid umtx_initImplPostInit(UInitOnce &uio) { 2412e5b6d6dSopenharmony_ci std::unique_lock<std::mutex> initLock(initMutex); 2422e5b6d6dSopenharmony_ci umtx_storeRelease(uio.fState, 2); 2432e5b6d6dSopenharmony_ci initCondition.notify_all(); 2442e5b6d6dSopenharmony_ci} 2452e5b6d6dSopenharmony_ci``` 246