• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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