Skip to content

Commit 344df34

Browse files
ICU-20570 Fix data race in initializeFCD (usearch.cpp) via UInitOnce
initializeFCD may be invoked concurrently from multiple threads, so the simple null-check on g_nfcImpl is a data race. Replace it with umtx_initOnce, which is designed exactly for this use case and is the pattern used throughout ICU. Ported from snowflake-eng/snowflake#461615.
1 parent 7edf6ee commit 344df34

1 file changed

Lines changed: 9 additions & 4 deletions

File tree

icu4c/source/i18n/usearch.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "cmemory.h"
2323
#include "ucln_in.h"
2424
#include "uassert.h"
25+
#include "umutex.h"
2526
#include "ustr_imp.h"
2627

2728
U_NAMESPACE_USE
@@ -33,6 +34,7 @@ U_NAMESPACE_USE
3334
#define SUPPLEMENTARY_MIN_VALUE_ 0x10000
3435

3536
static const Normalizer2Impl *g_nfcImpl = nullptr;
37+
static UInitOnce gSearchNFCInitOnce = U_INITONCE_INITIALIZER;
3638

3739
// internal methods -------------------------------------------------
3840

@@ -76,10 +78,16 @@ U_CDECL_BEGIN
7678
static UBool U_CALLCONV
7779
usearch_cleanup() {
7880
g_nfcImpl = nullptr;
81+
gSearchNFCInitOnce.reset();
7982
return true;
8083
}
8184
U_CDECL_END
8285

86+
static void U_CALLCONV initNFCImpl(UErrorCode *status) {
87+
g_nfcImpl = Normalizer2Factory::getNFCImpl(*status);
88+
ucln_i18n_registerCleanup(UCLN_I18N_USEARCH, usearch_cleanup);
89+
}
90+
8391
/**
8492
* Initializing the fcd tables.
8593
* Internal method, status assumed to be a success.
@@ -89,10 +97,7 @@ U_CDECL_END
8997
static
9098
inline void initializeFCD(UErrorCode *status)
9199
{
92-
if (g_nfcImpl == nullptr) {
93-
g_nfcImpl = Normalizer2Factory::getNFCImpl(*status);
94-
ucln_i18n_registerCleanup(UCLN_I18N_USEARCH, usearch_cleanup);
95-
}
100+
umtx_initOnce(gSearchNFCInitOnce, &initNFCImpl, status);
96101
}
97102

98103
/**

0 commit comments

Comments
 (0)