1 Note that programs that link ICU library may crash
2 because std::call_once cannot handle C++ exceptions.
3 The reason is that GCC's implementation calls pthread_once
4 that is not C++ exception aware at least in Solaris libc.
6 To workaround this issue, a simple implementation based
7 on C++11 atomic operations and variadic templates is provided.
8 This way we avoid that C++ exception is thrown in libc library.
10 Note that the implementation is far from optimal with respect
11 to the performance, for example, the following could be improved:
12 1) use a condition variable instead of yield if thread contention is high
13 2) utilize C++11 memory model to reduce synchronization overhead
15 However, the workaround is temporary as an untested patch is available on GCC's bugzilla:
16 https://gcc.gnu.org/pipermail/gcc-patches/2020-November/557928.html
18 The patch is not suitable for upstream.
19 It only fixes the problem in ICU library.
21 --- icu/source/common/umutex.cpp.orig
22 +++ icu/source/common/umutex.cpp
32 // Used when ICU implementation code passes nullptr for the mutex pointer.
35 +#if defined(sun) || defined(__sun) || defined(__sun__)
36 +enum class CallOnceState : int {
37 + INIT, RUNNING, FINISH
40 +std::atomic<CallOnceState> initState;
42 std::once_flag initFlag;
43 std::once_flag *pInitFlag = &initFlag;
46 } // Anonymous namespace
48 +#if defined(sun) || defined(__sun) || defined(__sun__)
49 +template <class Callable, class... Args>
50 +void solaris_call_once(std::atomic<CallOnceState>& state, Callable&& fce, Args&&... args) {
51 + if (state != CallOnceState::FINISH) {
53 + CallOnceState expected = CallOnceState::INIT, desired = CallOnceState::RUNNING;
54 + if (state.compare_exchange_strong(expected, desired)) {
56 + fce(std::forward<Args>(args)...);
57 + state = CallOnceState::FINISH;
59 + state = CallOnceState::INIT;
63 + std::this_thread::yield();
65 + } while (state != CallOnceState::FINISH);
71 static UBool U_CALLCONV umtx_cleanup() {
73 initCondition->~condition_variable();
76 + #if defined(sun) || defined(__sun) || defined(__sun__)
77 + initState = CallOnceState::INIT;
79 // Reset the once_flag, by destructing it and creating a fresh one in its place.
80 // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once().
81 pInitFlag->~once_flag();
82 pInitFlag = new(&initFlag) std::once_flag();
88 std::mutex *UMutex::getMutex() {
89 std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
90 if (retPtr == nullptr) {
91 + #if defined(sun) || defined(__sun) || defined(__sun__)
92 + solaris_call_once(initState, umtx_init);
94 std::call_once(*pInitFlag, umtx_init);
96 std::lock_guard<std::mutex> guard(*initMutex);
97 retPtr = fMutex.load(std::memory_order_acquire);
98 if (retPtr == nullptr) {
101 U_COMMON_API UBool U_EXPORT2
102 umtx_initImplPreInit(UInitOnce &uio) {
103 + #if defined(sun) || defined(__sun) || defined(__sun__)
104 + solaris_call_once(initState, umtx_init);
106 std::call_once(*pInitFlag, umtx_init);
108 std::unique_lock<std::mutex> lock(*initMutex);
109 if (umtx_loadAcquire(uio.fState) == 0) {
110 umtx_storeRelease(uio.fState, 1);