1--- 2layout: default 3title: Custom ICU4C Synchronization 4nav_order: 3 5parent: Misc 6--- 7<!-- 8© 2020 and later: Unicode, Inc. and others. 9License & terms of use: http://www.unicode.org/copyright.html 10--> 11 12# Custom ICU4C Synchronization 13{: .no_toc } 14 15## Contents 16{: .no_toc .text-delta } 17 181. TOC 19{:toc} 20 21--- 22 23> :warning: ***Support for including an alternate implementation of atomic and mutex 24> operations has been withdrawn and removed from ICU4C.*** 25> See issue [ICU-20185](https://unicode-org.atlassian.net/browse/ICU-20185). 26 27### Build Time User Provided Synchronization 28 29Build time user synchronization provides a mechanism for platforms with special 30requirements to provide their own mutex and one-time initialization 31implementations to ICU. This facility was introduced in ICU 53. It may change 32over time. 33 34The alternative implementations are compiled directly into the ICU libraries. 35Alternative implementations cannot be plugged in at run time. 36 37The tables below show the items that must be defined by a custom ICU 38synchronization implementation. The list includes both functions that are used 39throughout ICU code and additional functions are for internal by other ICU 40synchronization primitives. 41 42**Low Level Atomics**, a set of platform or compiler dependent typedefs and 43inlines. Provided in the internal header file 44[`umutex.h`](https://github.com/unicode-org/icu/blob/master/icu4c/source/common/umutex.h). 45 46| Type/Function | Description | 47|---------------------------------------------------------|-------------------------------------------------------------------------------| 48| `typedef u_atomic_int32_t` | A 32 bit integer that will work with low level atomic operations. (`typedef`) | 49| `umtx_loadAcquire(u_atomic_int32_t &var)` | | 50| `umtx_storeRelease(u_atomic_int32_t &var, int32_t val)` | | 51| `umtx_atomic_inc(u_atomic_int32_t &var)` | | 52| `umtx_atomic_dec(u_atomic_int32_t &var)` | | 53 54**Mutexes**. Type declarations for ICU mutex wrappers. Provided in a header file. 55 56| Type | Description | 57|-----------------------|---------------------------------------------------------------------------------------------------| 58| `struct UMutex` | An ICU mutex. All instances will be static. Typically just contains an underlying platform mutex. | 59| `U_MUTEX_INITIALIZER` | A C style initializer for a static instance of a `UMutex`. | 60 61**Mutex and InitOnce implementations**. Out-of-line platform-specific code. 62Provided in a .cpp file. 63 64| Function | Description | 65|-----------------------------------------|--------------------------------------------| 66| `umtx_lock(UMutex *mutex)` | Lock a mutex. | 67| `umtx_unlock(UMutex* mutex)` | Unlock a mutex. | 68| `umtx_initImplPreInit(UInitOnce &uio)` | `umtx_initOnce()` implementation function. | 69| `umtx_initImplPostInit(UInitOnce &uio)` | `umtx_initOnce()` implementation function. | 70 71`UInitOnce` and `umtx_initOnce()` are used internally by ICU for thread-safe 72one-time initialization. Their implementation is split into a 73platform-independent part (contained in 74[`umutex.h`](https://github.com/unicode-org/icu/blob/master/icu4c/source/common/umutex.h)), 75and the pair of platform-dependent implementation functions listed above. 76 77**Build Setup** 78 79Compiler preprocessor variables are used to name the custom files to be included 80in the ICU build. If defined, the files are included at the top of the normal 81platform `#ifdef` chains in the ICU sources, and effectively define a new 82platform. 83 84| Macro | Description | 85|--------------------|---------------------------------------------------------| 86| `U_USER_ATOMICS_H` | Set to the name of the low level atomics header file. | 87| `U_USER_MUTEX_H` | Mutexes header file. | 88| `U_USER_MUTEX_CPP` | Mutexes and `InitOnce` implementation file. | 89 90It is possible (and reasonable) to supply only the two mutex files, while 91retaining the ICU default implementation for the low level atomics. 92 93Example ICU configure with user mutexes specified: 94 95 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 96 97**Stability** 98 99This interface may change between ICU releases. The required set of functions 100may be extended, or details of the behavior required may be altered. 101 102The types and functions defined by this interface reach deeply into the ICU 103implementation, and we need to retain the ability to make changes should the 104need arise. 105 106**Examples** 107 108The code below shows a complete set of ICU user synchronization files. 109 110This implementation uses C++11 language mutexes and atomics. These make for a 111convenient reference implementation because the C++11 constructs are well 112defined and straight forward to use. 113 114Similar implementations for POSIX and Windows can be found in files 115`common/umutex.h` and `common/umutex.cpp`, in the platform `#ifdef` chains; these are 116part of the standard ICU distribution. 117 118**Mutex Header** 119```c++ 120// Example of an ICU build time customized mutex header. 121// 122// Must define struct UMutex and an initializer that will work with static instances. 123// All UMutex instances in ICU code will be static. 124 125#ifndef ICU_MUTEX_C11_H 126#define ICU_MUTEX_C11_H 127#include <mutex> 128#include <condition_variable> 129struct UMutex { 130 std::mutex fMutex; 131}; 132#define U_MUTEX_INITIALIZER {} 133#endif 134``` 135 136**Atomics Header** 137```c++ 138#include <atomic> 139 140typedef std::atomic<int32_t> u_atomic_int32_t; 141#define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) 142 143inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { 144 return var.load(std::memory_order_acquire); 145} 146 147inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 148 var.store(val, std::memory_order_release); 149} 150 151inline int32_t umtx_atomic_inc(u_atomic_int32_t &var) { 152 return var.fetch_add(1) + 1; 153} 154 155inline int32_t umtx_atomic_dec(u_atomic_int32_t &var) { 156 return var.fetch_sub(1) - 1; 157} 158``` 159 160**Mutex and InitOnce implementations** 161```c++ 162// 163// Example ICU build time custom mutex cpp file. 164// 165// Must implement these functions: 166// umtx_lock(UMutex *mutex); 167// umtx_unlock(UMutex *mutex); 168// umtx_initImplPreInit(UInitOnce &uio); 169// umtx_initImplPostInit(UInitOnce &uio); 170 171U_CAPI void U_EXPORT2 172umtx_lock(UMutex *mutex) { 173 if (mutex == NULL) { 174 // Note: globalMutex is pre-defined in the platform-independent ICU code. 175 mutex = &globalMutex; 176 } 177 mutex->fMutex.lock(); 178} 179 180U_CAPI void U_EXPORT2 181umtx_unlock(UMutex* mutex) `{ 182 if (mutex == NULL) { 183 mutex = &globalMutex; 184 } 185 mutex->fMutex.unlock(); 186} 187 188// A mutex and a condition variable are used by the implementation of umtx_initOnce() 189// The mutex is held only while the state of the InitOnce object is being changed or 190// tested. It is not held while initialization functions are running. 191// Threads needing to block, waiting for an initialization to complete, will wait 192// on the condition variable. 193// All InitOnce objects share a common mutex and condition variable. This means that 194// all blocked threads will wake if any (possibly unrelated) initialization completes. 195// Which does no harm, it should be statistically rare, and any spuriously woken 196// threads will check their state and promptly wait again. 197 198static std::mutex initMutex; 199static std::condition_variable initCondition; 200 201// This function is called from umtx_initOnce() when an initial test of a UInitOnce::fState flag 202// reveals that initialization has not completed, that we either need to call the 203// function on this thread, or wait for some other thread to complete the initialization. 204// 205// The actual call to the init function is made inline by template code 206// that knows the C++ types involved. This function returns TRUE if 207// the inline code needs to invoke the Init function, or FALSE if the initialization 208// has completed on another thread. 209// 210// UInitOnce::fState values: 211// 0: Initialization has not yet begun. 212// 1: Initialization is in progress, not yet complete. 213// 2: Initialization is complete. 214// 215UBool umtx_initImplPreInit(UInitOnce &uio) { 216 std::unique_lock<std::mutex> initLock(initMutex); 217 int32_t state = uio.fState; 218 if (state == 0) { 219 umtx_storeRelease(uio.fState, 1); 220 return TRUE; // Caller will next call the init function. 221 } else { 222 while (uio.fState == 1) { 223 // Another thread is currently running the initialization. 224 // Wait until it completes. 225 initCondition.wait(initLock); 226 } 227 U_ASSERT(uio.fState == 2); 228 return FALSE; 229 } 230} 231 232// This function is called from umtx_initOnce() just after an initializationfunction completes. 233// Its purpose is to set the state of the UInitOnce object to initialized, and to 234// unblock any threads that may be waiting on the initialization. 235// 236// Some threads may be waiting on the condition variable, requiring the notify_all(). 237// Some threads may be racing to test the fState flag outside of the mutex, 238// requiring the use of store-release when changing its value. 239 240void umtx_initImplPostInit(UInitOnce &uio) { 241 std::unique_lock<std::mutex> initLock(initMutex); 242 umtx_storeRelease(uio.fState, 2); 243 initCondition.notify_all(); 244} 245``` 246