From 2165cb86e0dad18615b084b151bc50ebc837601e Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Wed, 7 Aug 2024 02:05:55 +0900 Subject: [PATCH] Apply WASI support patch against ICU source tree (#35) * Cherry-pick "ICU-22838 Add WebAssembly/WASI cross-compilation support" The original patch is still under review in the upstream ICU project, but it is needed to unblock the swift-foundation build on WebAssembly. See https://github.com/unicode-org/icu/pull/3067 * [Build] Update compile definitions for WASI target `U_TIMEZONE` must not be defined and dynamic loading features must be disabled for WASI target. --- CMakeLists.txt | 14 +++-- Package.swift | 4 ++ icuSources/common/putilimp.h | 6 ++ icuSources/common/umutex.cpp | 20 +++++++ icuSources/common/umutex.h | 47 +++++++++++++-- icuSources/common/unifiedcache.cpp | 59 ++++++++++++++----- icuSources/i18n/decimfmt.cpp | 28 +++++++++ icuSources/i18n/number_mapper.h | 12 +++- icuSources/i18n/numrange_fluent.cpp | 31 ++++++++++ .../numberrangeformatter.h | 8 +++ .../include/_foundation_unicode/platform.h | 13 ++++ 11 files changed, 216 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 725d2a5..8f371b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,20 +57,22 @@ if(CMAKE_SYSTEM_NAME STREQUAL Windows) $<$:U_TIMEZONE=_timezone> $<$:_CRT_SECURE_NO_DEPRECATE> $<$:U_PLATFORM_USES_ONLY_WIN32_API>) -else() - add_compile_definitions( - $<$:U_TIMEZONE=timezone>) -endif() -# WASI specific settings -if(CMAKE_SYSTEM_NAME STREQUAL WASI) +elseif(CMAKE_SYSTEM_NAME STREQUAL WASI) + # WASI specific settings add_compile_definitions( $<$:U_HAVE_TZSET=0> $<$:U_HAVE_TZNAME=0> $<$:U_HAVE_TIMEZONE=0> + $<$:HAVE_DLFCN_H=0> + $<$:HAVE_DLOPEN=0> + $<$:U_ENABLE_DYLOAD=0> $<$:_WASI_EMULATED_SIGNAL> $<$:_WASI_EMULATED_MMAN>) add_link_options("-Lwasi-emulated-signal") add_link_options("-Lwasi-emulated-mman") +else() + add_compile_definitions( + $<$:U_TIMEZONE=timezone>) endif() if(BUILD_SHARED_LIBS) diff --git a/Package.swift b/Package.swift index c174df1..f174d20 100644 --- a/Package.swift +++ b/Package.swift @@ -38,6 +38,10 @@ var buildSettings: [CXXSetting] = [ .define("U_COMMON_IMPLEMENTATION"), .define("U_I18N_IMPLEMENTATION"), .define("U_IO_IMPLEMENTATION"), + .define("HAVE_DLFCN_H", to: "0", .when(platforms: [.wasi])), + .define("HAVE_DLOPEN", to: "0", .when(platforms: [.wasi])), + .define("U_ENABLE_DYLOAD", to: "0", .when(platforms: [.wasi])), + // Where data are stored .define("ICU_DATA_DIR", to: "\"/usr/share/icu/\""), .define("USE_PACKAGE_DATA", to: "1"), diff --git a/icuSources/common/putilimp.h b/icuSources/common/putilimp.h index 2c74df8..106fef4 100644 --- a/icuSources/common/putilimp.h +++ b/icuSources/common/putilimp.h @@ -103,6 +103,8 @@ typedef size_t uintptr_t; #endif #elif U_PLATFORM == U_PF_OS400 /* not defined */ +#elif defined(__wasi__) + /* not defined */ #else # define U_TZSET tzset #endif @@ -128,6 +130,8 @@ typedef size_t uintptr_t; /* not defined */ #elif U_PLATFORM == U_PF_IPHONE /* not defined */ +#elif defined(__wasi__) + /* not defined */ #else # define U_TIMEZONE timezone #endif @@ -141,6 +145,8 @@ typedef size_t uintptr_t; #endif #elif U_PLATFORM == U_PF_OS400 /* not defined */ +#elif defined(__wasi__) + /* not defined */ #else # define U_TZNAME tzname #endif diff --git a/icuSources/common/umutex.cpp b/icuSources/common/umutex.cpp index 26d2d7e..8749e2e 100644 --- a/icuSources/common/umutex.cpp +++ b/icuSources/common/umutex.cpp @@ -43,6 +43,7 @@ U_NAMESPACE_BEGIN * *************************************************************************************************/ +#if U_HAVE_ATOMICS namespace { std::mutex *initMutex; std::condition_variable *initCondition; @@ -55,9 +56,11 @@ std::once_flag initFlag; std::once_flag *pInitFlag = &initFlag; } // Anonymous namespace +#endif U_CDECL_BEGIN static UBool U_CALLCONV umtx_cleanup() { +#if U_HAVE_ATOMICS initMutex->~mutex(); initCondition->~condition_variable(); UMutex::cleanup(); @@ -66,17 +69,21 @@ static UBool U_CALLCONV umtx_cleanup() { // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once(). pInitFlag->~once_flag(); pInitFlag = new(&initFlag) std::once_flag(); +#endif return true; } static void U_CALLCONV umtx_init() { +#if U_HAVE_ATOMICS initMutex = STATIC_NEW(std::mutex); initCondition = STATIC_NEW(std::condition_variable); ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); +#endif } U_CDECL_END +#if U_HAVE_ATOMICS std::mutex *UMutex::getMutex() { std::mutex *retPtr = fMutex.load(std::memory_order_acquire); if (retPtr == nullptr) { @@ -93,14 +100,17 @@ std::mutex *UMutex::getMutex() { U_ASSERT(retPtr != nullptr); return retPtr; } +#endif UMutex *UMutex::gListHead = nullptr; void UMutex::cleanup() { UMutex *next = nullptr; for (UMutex *m = gListHead; m != nullptr; m = next) { +#if U_HAVE_ATOMICS (*m->fMutex).~mutex(); m->fMutex = nullptr; +#endif next = m->fListLink; m->fListLink = nullptr; } @@ -110,20 +120,24 @@ void UMutex::cleanup() { U_CAPI void U_EXPORT2 umtx_lock(UMutex *mutex) { +#if U_HAVE_ATOMICS if (mutex == nullptr) { mutex = &globalMutex; } mutex->lock(); +#endif } U_CAPI void U_EXPORT2 umtx_unlock(UMutex* mutex) { +#if U_HAVE_ATOMICS if (mutex == nullptr) { mutex = &globalMutex; } mutex->unlock(); +#endif } @@ -143,18 +157,22 @@ umtx_unlock(UMutex* mutex) // U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { +#if U_HAVE_ATOMICS std::call_once(*pInitFlag, umtx_init); std::unique_lock lock(*initMutex); +#endif if (umtx_loadAcquire(uio.fState) == 0) { umtx_storeRelease(uio.fState, 1); return true; // Caller will next call the init function. } else { +#if U_HAVE_ATOMICS while (umtx_loadAcquire(uio.fState) == 1) { // Another thread is currently running the initialization. // Wait until it completes. initCondition->wait(lock); } U_ASSERT(uio.fState == 2); +#endif return false; } } @@ -168,11 +186,13 @@ umtx_initImplPreInit(UInitOnce &uio) { U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { +#if U_HAVE_ATOMICS { std::unique_lock lock(*initMutex); umtx_storeRelease(uio.fState, 2); } initCondition->notify_all(); +#endif } U_NAMESPACE_END diff --git a/icuSources/common/umutex.h b/icuSources/common/umutex.h index 731e77c..4b379a6 100644 --- a/icuSources/common/umutex.h +++ b/icuSources/common/umutex.h @@ -20,9 +20,6 @@ #ifndef UMUTEX_H #define UMUTEX_H -#include -#include -#include #include #include <_foundation_unicode/utypes.h> @@ -37,6 +34,12 @@ #error U_USER_ATOMICS and U_USER_MUTEX_H are not supported #endif +#if U_HAVE_ATOMICS + +#include +#include +#include + // Export an explicit template instantiation of std::atomic. // When building DLLs for Windows this is required as it is used as a data member of the exported SharedObject class. // See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples. @@ -61,6 +64,7 @@ template struct std::atomic; #endif #endif +#endif U_NAMESPACE_BEGIN @@ -70,6 +74,8 @@ U_NAMESPACE_BEGIN * ****************************************************************************/ +#if U_HAVE_ATOMICS + typedef std::atomic u_atomic_int32_t; inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { @@ -88,6 +94,29 @@ inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { return var->fetch_sub(1) - 1; } +#else + +// No atomic operations available. Use a simple int32_t instead. + +typedef int32_t u_atomic_int32_t; + +inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) { + return var; +} + +inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { + var = val; +} + +inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) { + return ++(*var); +} + +inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { + return --(*var); +} + +#endif /************************************************************************************************* * @@ -227,17 +256,25 @@ class U_COMMON_API UMutex { // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard void lock() { +#if U_HAVE_ATOMICS std::mutex *m = fMutex.load(std::memory_order_acquire); if (m == nullptr) { m = getMutex(); } m->lock(); +#endif + } + void unlock() { +#if U_HAVE_ATOMICS + fMutex.load(std::memory_order_relaxed)->unlock(); +#endif } - void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); } static void cleanup(); private: +#if U_HAVE_ATOMICS alignas(std::mutex) char fStorage[sizeof(std::mutex)] {}; std::atomic fMutex { nullptr }; +#endif /** All initialized UMutexes are kept in a linked list, so that they can be found, * and the underlying std::mutex destructed, by u_cleanup(). @@ -249,7 +286,9 @@ class U_COMMON_API UMutex { * Initial fast check is inline, in lock(). The returned value may never * be nullptr. */ +#if U_HAVE_ATOMICS std::mutex *getMutex(); +#endif }; diff --git a/icuSources/common/unifiedcache.cpp b/icuSources/common/unifiedcache.cpp index 1284c03..df7b11d 100644 --- a/icuSources/common/unifiedcache.cpp +++ b/icuSources/common/unifiedcache.cpp @@ -13,15 +13,42 @@ #include "unifiedcache.h" #include // For std::max() +#if U_HAVE_ATOMICS #include +#endif #include "uassert.h" #include "uhash.h" #include "ucln_cmn.h" static icu::UnifiedCache *gCache = nullptr; +#if U_HAVE_ATOMICS static std::mutex *gCacheMutex = nullptr; static std::condition_variable *gInProgressValueAddedCond; + +static void unifiedcache_init_mutex() { + gCacheMutex = STATIC_NEW(std::mutex); + gInProgressValueAddedCond = STATIC_NEW(std::condition_variable); +} + +static void unifiedcache_cleanup_mutex() { + gCacheMutex->~mutex(); + gCacheMutex = nullptr; + gInProgressValueAddedCond->~condition_variable(); + gInProgressValueAddedCond = nullptr; +} + +#define UNIFIED_CACHE_LOCK_GUARD std::lock_guard lock(*gCacheMutex) + +#else +// No atomics, no mutexes. +static void unifiedcache_init_mutex() {} +static void unifiedcache_cleanup_mutex() {} + +#define UNIFIED_CACHE_LOCK_GUARD do {} while (0) + +#endif + static icu::UInitOnce gCacheInitOnce {}; static const int32_t MAX_EVICT_ITERATIONS = 10; @@ -34,10 +61,7 @@ static UBool U_CALLCONV unifiedcache_cleanup() { gCacheInitOnce.reset(); delete gCache; gCache = nullptr; - gCacheMutex->~mutex(); - gCacheMutex = nullptr; - gInProgressValueAddedCond->~condition_variable(); - gInProgressValueAddedCond = nullptr; + unifiedcache_cleanup_mutex(); return true; } U_CDECL_END @@ -72,8 +96,7 @@ static void U_CALLCONV cacheInit(UErrorCode &status) { ucln_common_registerCleanup( UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup); - gCacheMutex = STATIC_NEW(std::mutex); - gInProgressValueAddedCond = STATIC_NEW(std::condition_variable); + unifiedcache_init_mutex(); gCache = new UnifiedCache(status); if (gCache == nullptr) { status = U_MEMORY_ALLOCATION_ERROR; @@ -135,28 +158,28 @@ void UnifiedCache::setEvictionPolicy( status = U_ILLEGAL_ARGUMENT_ERROR; return; } - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; fMaxUnused = count; fMaxPercentageOfInUse = percentageOfInUseItems; } int32_t UnifiedCache::unusedCount() const { - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; return uhash_count(fHashtable) - fNumValuesInUse; } int64_t UnifiedCache::autoEvictedCount() const { - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; return fAutoEvictedCount; } int32_t UnifiedCache::keyCount() const { - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; return uhash_count(fHashtable); } void UnifiedCache::flush() const { - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; // Use a loop in case cache items that are flushed held hard references to // other cache items making those additional cache items eligible for @@ -165,7 +188,7 @@ void UnifiedCache::flush() const { } void UnifiedCache::handleUnreferencedObject() const { - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; --fNumValuesInUse; _runEvictionSlice(); } @@ -184,7 +207,7 @@ void UnifiedCache::dump() { } void UnifiedCache::dumpContents() const { - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; _dumpContents(); } @@ -224,7 +247,7 @@ UnifiedCache::~UnifiedCache() { // Now all that should be left in the cache are entries that refer to // each other and entries with hard references from outside the cache. // Nothing we can do about these so proceed to wipe out the cache. - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; _flush(true); } uhash_close(fHashtable); @@ -325,7 +348,7 @@ void UnifiedCache::_putIfAbsentAndGet( const CacheKeyBase &key, const SharedObject *&value, UErrorCode &status) const { - std::lock_guard lock(*gCacheMutex); + UNIFIED_CACHE_LOCK_GUARD; const UHashElement *element = uhash_find(fHashtable, &key); if (element != nullptr && !_inProgress(element)) { _fetch(element, value, status); @@ -350,14 +373,18 @@ UBool UnifiedCache::_poll( UErrorCode &status) const { U_ASSERT(value == nullptr); U_ASSERT(status == U_ZERO_ERROR); +#if U_HAVE_ATOMICS std::unique_lock lock(*gCacheMutex); +#endif const UHashElement *element = uhash_find(fHashtable, &key); // If the hash table contains an inProgress placeholder entry for this key, // this means that another thread is currently constructing the value object. // Loop, waiting for that construction to complete. while (element != nullptr && _inProgress(element)) { +#if U_HAVE_ATOMICS gInProgressValueAddedCond->wait(lock); +#endif element = uhash_find(fHashtable, &key); } @@ -428,9 +455,11 @@ void UnifiedCache::_put( U_ASSERT(oldValue == fNoValue); removeSoftRef(oldValue); +#if U_HAVE_ATOMICS // Tell waiting threads that we replace in-progress status with // an error. gInProgressValueAddedCond->notify_all(); +#endif } void UnifiedCache::_fetch( diff --git a/icuSources/i18n/decimfmt.cpp b/icuSources/i18n/decimfmt.cpp index fc51e23..5301293 100644 --- a/icuSources/i18n/decimfmt.cpp +++ b/icuSources/i18n/decimfmt.cpp @@ -504,8 +504,13 @@ DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { DecimalFormat::~DecimalFormat() { if (fields == nullptr) { return; } +#if U_HAVE_ATOMICS delete fields->atomicParser.exchange(nullptr); delete fields->atomicCurrencyParser.exchange(nullptr); +#else + delete fields->atomicParser; + delete fields->atomicCurrencyParser; +#endif delete fields; } @@ -1734,8 +1739,13 @@ void DecimalFormat::touch(UErrorCode& status) { setupFastFormat(); // Delete the parsers if they were made previously +#if U_HAVE_ATOMICS delete fields->atomicParser.exchange(nullptr); delete fields->atomicCurrencyParser.exchange(nullptr); +#else + delete fields->atomicParser; + delete fields->atomicCurrencyParser; +#endif // In order for the getters to work, we need to populate some fields in NumberFormat. #if APPLE_ICU_CHANGES @@ -1789,7 +1799,11 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& sta } // First try to get the pre-computed parser +#if U_HAVE_ATOMICS auto* ptr = fields->atomicParser.load(); +#else + auto* ptr = fields->atomicParser; +#endif if (ptr != nullptr) { return ptr; } @@ -1808,6 +1822,7 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& sta // it is set to what is actually stored in the atomic // if another thread beat us to computing the parser object. auto* nonConstThis = const_cast(this); +#if U_HAVE_ATOMICS if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the parser delete temp; @@ -1816,13 +1831,21 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& sta // Our copy of the parser got stored in the atomic return temp; } +#else + nonConstThis->fields->atomicParser = temp; + return temp; +#endif } const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const { if (U_FAILURE(status)) { return nullptr; } // First try to get the pre-computed parser +#if U_HAVE_ATOMICS auto* ptr = fields->atomicCurrencyParser.load(); +#else + auto* ptr = fields->atomicCurrencyParser; +#endif if (ptr != nullptr) { return ptr; } @@ -1837,6 +1860,7 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorC // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the // atomic if another thread beat us to computing the parser object. auto* nonConstThis = const_cast(this); +#if U_HAVE_ATOMICS if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the parser delete temp; @@ -1845,6 +1869,10 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorC // Our copy of the parser got stored in the atomic return temp; } +#else + nonConstThis->fields->atomicCurrencyParser = temp; + return temp; +#endif } void diff --git a/icuSources/i18n/number_mapper.h b/icuSources/i18n/number_mapper.h index 7f3b4ba..9a60574 100644 --- a/icuSources/i18n/number_mapper.h +++ b/icuSources/i18n/number_mapper.h @@ -7,7 +7,9 @@ #ifndef __NUMBER_MAPPER_H__ #define __NUMBER_MAPPER_H__ -#include +#if U_HAVE_ATOMICS +# include +#endif #include "number_types.h" #include <_foundation_unicode/currpinf.h> #include "standardplural.h" @@ -198,10 +200,18 @@ struct DecimalFormatFields : public UMemory { LocalizedNumberFormatter formatter; /** The lazy-computed parser for .parse() */ +#if U_HAVE_ATOMICS std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicParser = {}; +#else + ::icu::numparse::impl::NumberParserImpl* atomicParser = nullptr; +#endif /** The lazy-computed parser for .parseCurrency() */ +#if U_HAVE_ATOMICS std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicCurrencyParser = {}; +#else + ::icu::numparse::impl::NumberParserImpl* atomicCurrencyParser = {}; +#endif /** Small object ownership warehouse for the formatter and parser */ DecimalFormatWarehouse warehouse; diff --git a/icuSources/i18n/numrange_fluent.cpp b/icuSources/i18n/numrange_fluent.cpp index 98fd6fe..ea874cf 100644 --- a/icuSources/i18n/numrange_fluent.cpp +++ b/icuSources/i18n/numrange_fluent.cpp @@ -240,29 +240,51 @@ LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS&& src) noe : NFS(std::move(src)) { // Steal the compiled formatter LNF&& _src = static_cast(src); +#if U_HAVE_ATOMICS auto* stolen = _src.fAtomicFormatter.exchange(nullptr); delete fAtomicFormatter.exchange(stolen); +#else + delete fAtomicFormatter; + fAtomicFormatter = _src.fAtomicFormatter; + _src.fAtomicFormatter = nullptr; +#endif } LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) { if (this == &other) { return *this; } // self-assignment: no-op NFS::operator=(static_cast&>(other)); +#if U_HAVE_ATOMICS // Do not steal; just clear delete fAtomicFormatter.exchange(nullptr); +#else + delete fAtomicFormatter; + fAtomicFormatter = nullptr; +#endif return *this; } LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) noexcept { NFS::operator=(static_cast&&>(src)); +#if U_HAVE_ATOMICS // Steal the compiled formatter auto* stolen = src.fAtomicFormatter.exchange(nullptr); delete fAtomicFormatter.exchange(stolen); +#else + delete fAtomicFormatter; + fAtomicFormatter = src.fAtomicFormatter; + src.fAtomicFormatter = nullptr; +#endif return *this; } LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() { +#if U_HAVE_ATOMICS delete fAtomicFormatter.exchange(nullptr); +#else + delete fAtomicFormatter; + fAtomicFormatter = nullptr; +#endif } LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) { @@ -346,7 +368,11 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { } // First try to get the pre-computed formatter +#if U_HAVE_ATOMICS auto* ptr = fAtomicFormatter.load(); +#else + auto* ptr = fAtomicFormatter; +#endif if (ptr != nullptr) { return ptr; } @@ -366,6 +392,7 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { // it is set to what is actually stored in the atomic // if another thread beat us to computing the formatter object. auto* nonConstThis = const_cast(this); +#if U_HAVE_ATOMICS if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the formatter delete temp; @@ -374,6 +401,10 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { // Our copy of the formatter got stored in the atomic return temp; } +#else + nonConstThis->fAtomicFormatter = temp; + return temp; +#endif } diff --git a/icuSources/include/_foundation_unicode/numberrangeformatter.h b/icuSources/include/_foundation_unicode/numberrangeformatter.h index d5befe7..36a5f34 100644 --- a/icuSources/include/_foundation_unicode/numberrangeformatter.h +++ b/icuSources/include/_foundation_unicode/numberrangeformatter.h @@ -10,7 +10,9 @@ #if !UCONFIG_NO_FORMATTING +#if U_HAVE_ATOMICS #include +#endif #include <_foundation_unicode/appendable.h> #include <_foundation_unicode/fieldpos.h> #include <_foundation_unicode/formattedvalue.h> @@ -77,7 +79,9 @@ struct UFormattedNumberRangeImpl; } // namespace icu::number U_NAMESPACE_END +#if U_HAVE_ATOMICS template struct U_I18N_API std::atomic< U_NAMESPACE_QUALIFIER number::impl::NumberRangeFormatterImpl*>; +#endif U_NAMESPACE_BEGIN namespace number { // icu::number @@ -553,7 +557,11 @@ class U_I18N_API LocalizedNumberRangeFormatter ~LocalizedNumberRangeFormatter(); private: +#if U_HAVE_ATOMICS std::atomic fAtomicFormatter = {}; +#else + impl::NumberRangeFormatterImpl* fAtomicFormatter = nullptr; +#endif const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) const; diff --git a/icuSources/include/_foundation_unicode/platform.h b/icuSources/include/_foundation_unicode/platform.h index 8b22e35..b00dc66 100644 --- a/icuSources/include/_foundation_unicode/platform.h +++ b/icuSources/include/_foundation_unicode/platform.h @@ -347,6 +347,19 @@ # define U_HAVE_INTTYPES_H U_HAVE_STDINT_H #endif +/** + * \def U_HAVE_ATOMICS + * Defines whether the platform supports atomic operations. + */ +#ifdef U_HAVE_ATOMICS + /* Use the predefined value. */ +#elif defined(__wasi__) && !defined(_REENTRANT) + /* WASI does not support atomics when wasi-threads feature is not enabled */ +# define U_HAVE_ATOMICS 0 +#else +# define U_HAVE_ATOMICS 1 +#endif + /*===========================================================================*/ /** @{ Compiler and environment features */ /*===========================================================================*/