diff --git a/eng/Versions.props b/eng/Versions.props index b2e4931d09e..aba8d06cd49 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,4 +8,7 @@ 8.0.0-beta.23067.5 + + 16 + diff --git a/eng/azure-pipelines.yml b/eng/azure-pipelines.yml index 8a4e2cb750a..6f4e35baa89 100644 --- a/eng/azure-pipelines.yml +++ b/eng/azure-pipelines.yml @@ -77,6 +77,51 @@ stages: artifactName: Artifacts_Browser_Threading condition: and(succeeded(), ne(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) + ############ WASI BUILD ############ + - job: Build_Wasi + displayName: Wasi + timeoutInMinutes: 360 + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + vmImage: ubuntu-20.04 + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: NetCore1ESPool-Internal + demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 + container: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-webassembly + steps: + - bash: | + ./build.sh --ci --restore --build --configuration $(_BuildConfig) /p:TargetOS=Wasi /p:TargetArchitecture=wasm $(_InternalBuildArgs) + displayName: Build + - task: PublishPipelineArtifact@1 + displayName: Upload artifacts + inputs: + targetPath: 'artifacts/bin' + artifactName: Artifacts_Wasi + condition: and(succeeded(), ne(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) + + - job: Build_Wasi_Threading + displayName: Wasi_Threading + timeoutInMinutes: 360 + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + vmImage: ubuntu-20.04 + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: NetCore1ESPool-Internal + demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 + container: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-webassembly + steps: + - bash: | + ./build.sh --ci --restore --build --configuration $(_BuildConfig) /p:TargetOS=Wasi /p:TargetArchitecture=wasm /p:WasmEnableThreads=true $(_InternalBuildArgs) + displayName: Build + - task: PublishPipelineArtifact@1 + displayName: Upload artifacts + inputs: + targetPath: 'artifacts/bin' + artifactName: Artifacts_Wasi_Threading + condition: and(succeeded(), ne(variables['System.TeamProject'], 'public'), ne(variables['Build.Reason'], 'PullRequest')) + ############ iOS BUILD ############ - job: Build_iOS displayName: iOS @@ -168,6 +213,8 @@ stages: dependsOn: - Build_Browser - Build_Browser_Threading + - Build_Wasi + - Build_Wasi_Threading - Build_iOS - Build_MacCatalyst - Build_tvOS @@ -188,6 +235,14 @@ stages: inputs: artifact: Artifacts_Browser_Threading path: 'artifacts/bin/browser-threading' + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Artifacts_Wasi + path: 'artifacts/bin' + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Artifacts_Wasi_Threading + path: 'artifacts/bin/wasi-threading' - task: DownloadPipelineArtifact@2 inputs: artifact: Artifacts_iOS diff --git a/eng/icu.mk b/eng/icu.mk index 0cd7ce0fa69..bee46795c07 100644 --- a/eng/icu.mk +++ b/eng/icu.mk @@ -91,8 +91,8 @@ data-$(2): $(TARGET_OBJDIR)/$(1)/.stamp-configure | $(TARGET_OBJDIR)/$(1) $(TARG endef -ifeq ($(TARGET_OS),browser) -$(eval $(call TargetBuildTemplate,icudt_browser,icudt)) +ifeq ($(TARGET_ARCHITECTURE),wasm) +$(eval $(call TargetBuildTemplate,icudt_wasm,icudt)) else $(eval $(call TargetBuildTemplate,icudt_mobile,icudt)) endif diff --git a/eng/icu.proj b/eng/icu.proj index da08c33e49d..ffb4d60dc7f 100644 --- a/eng/icu.proj +++ b/eng/icu.proj @@ -1,5 +1,7 @@ - + + + @@ -7,10 +9,15 @@ <_ExtraParams Condition="'$(WasmEnableThreads)' == 'true'">WASM_ENABLE_THREADS=true + + + $(RepoRoot)artifacts/wasi-sdk + + @@ -37,4 +44,19 @@ Lines="@(_ItemsFromEmscriptenVersionFile->Replace('"', ''))" Overwrite="true" /> + + + + + <_WasiLocalPath>$(RepoRoot)\artifacts + <_WasiSdkUrl Condition="$([MSBuild]::IsOSPlatform('Linux'))">https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(WasiSdkVersion)/wasi-sdk-$(WasiSdkVersion).0-linux.tar.gz + <_WasiSdkUrl Condition="$([MSBuild]::IsOSPlatform('OSX'))">https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$(WasiSdkVersion)/wasi-sdk-$(WasiSdkVersion).0-macos.tar.gz + + + + + + diff --git a/eng/icu.wasi.mk b/eng/icu.wasi.mk new file mode 100644 index 00000000000..199ba8898e8 --- /dev/null +++ b/eng/icu.wasi.mk @@ -0,0 +1,29 @@ +# TODO: add other host platform/arch combinations +UNAME_S := $(shell uname -s) +UNAME_M := $(shell uname -m) +UNAME_R := $(shell uname -r) + +ifeq ($(UNAME_S),Linux) + HOST_PLATFORM=x86_64-pc-linux-gnu +endif +ifeq ($(UNAME_S),Darwin) + ifeq ($(UNAME_M),arm64) + HOST_PLATFORM=arm64-apple-darwin$(UNAME_R) + endif + ifeq ($(UNAME_M),x86_64) + HOST_PLATFORM=x86_64-apple-darwin$(UNAME_R) + endif +endif +ifeq ($(WASM_ENABLE_THREADS),true) + THREADS_FLAG="-pthread" +endif + +CONFIGURE_COMPILER_FLAGS += \ + CFLAGS="-Oz -fno-exceptions -Wno-sign-compare $(THREADS_FLAG) $(ICU_DEFINES)" \ + CXXFLAGS="-Oz -fno-exceptions -Wno-sign-compare $(THREADS_FLAG) $(ICU_DEFINES)" \ + CC="$(WASI_SDK_PATH)/bin/clang --sysroot=$(WASI_SDK_PATH)/share/wasi-sysroot" \ + CXX="$(WASI_SDK_PATH)/bin/clang++ --sysroot=$(WASI_SDK_PATH)/share/wasi-sysroot" \ + --host=$(HOST_PLATFORM) --build=wasm32 + +check-env: + : \ No newline at end of file diff --git a/icu-filters/icudt_browser.json b/icu-filters/icudt_wasm.json similarity index 100% rename from icu-filters/icudt_browser.json rename to icu-filters/icudt_wasm.json diff --git a/icu-patches/patches/020-MSFT-Patch-ICU_wasi_workaround.patch b/icu-patches/patches/020-MSFT-Patch-ICU_wasi_workaround.patch new file mode 100644 index 00000000000..c5732f589f1 --- /dev/null +++ b/icu-patches/patches/020-MSFT-Patch-ICU_wasi_workaround.patch @@ -0,0 +1,714 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Meri Khamoyan +Date: Thu, 19 Jan 2023 18:21:31 +0100 +Subject: Fix icu for wasi +# Handle WASI lack of support for and . +# +# WASI issue: https://github.com/WebAssembly/wasi-sdk/issues/180 + +diff --git a/icu/icu4c/source/common/putilimp.h b/icu/icu4c/source/common/putilimp.h +index a325c6c359ad26f1c7202ff4700afa39b5d284c3..ab5e8ac68ad6c1e92feecce83ef0b42c21450115 100644 +--- a/icu/icu4c/source/common/putilimp.h ++++ b/icu/icu4c/source/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/icu/icu4c/source/common/umapfile.h b/icu/icu4c/source/common/umapfile.h +index 92bd567a2a98952845eb489391092a6a64d9dfbc..4ed1112bc6dfaf96e6d106d4fead783229a87ab5 100644 +--- a/icu/icu4c/source/common/umapfile.h ++++ b/icu/icu4c/source/common/umapfile.h +@@ -41,6 +41,8 @@ U_CFUNC void uprv_unmapFile(UDataMemory *pData); + + #if UCONFIG_NO_FILE_IO + # define MAP_IMPLEMENTATION MAP_NONE ++#elif defined(__wasi__) ++# define MAP_IMPLEMENTATION MAP_STDIO + #elif U_PLATFORM_USES_ONLY_WIN32_API + # define MAP_IMPLEMENTATION MAP_WIN32 + #elif U_HAVE_MMAP || U_PLATFORM == U_PF_OS390 +diff --git a/icu/icu4c/source/common/umutex.cpp b/icu/icu4c/source/common/umutex.cpp +index ccbee9960a39e7bea47b4f76a41e30ae671e5f0f..729644eed9c93699db2676c3b8d20878d886ba38 100644 +--- a/icu/icu4c/source/common/umutex.cpp ++++ b/icu/icu4c/source/common/umutex.cpp +@@ -42,7 +42,7 @@ U_NAMESPACE_BEGIN + * ICU Mutex wrappers. + * + *************************************************************************************************/ +- ++#ifndef __wasi__ + namespace { + std::mutex *initMutex; + std::condition_variable *initCondition; +@@ -55,9 +55,11 @@ std::once_flag initFlag; + std::once_flag *pInitFlag = &initFlag; + + } // Anonymous namespace ++#endif + + U_CDECL_BEGIN + static UBool U_CALLCONV umtx_cleanup() { ++#ifndef __wasi__ + initMutex->~mutex(); + initCondition->~condition_variable(); + UMutex::cleanup(); +@@ -66,17 +68,20 @@ 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() { ++#ifndef __wasi__ + initMutex = STATIC_NEW(std::mutex); + initCondition = STATIC_NEW(std::condition_variable); + ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); ++#endif + } + U_CDECL_END + +- ++#ifndef __wasi__ + std::mutex *UMutex::getMutex() { + std::mutex *retPtr = fMutex.load(std::memory_order_acquire); + if (retPtr == nullptr) { +@@ -93,14 +98,16 @@ 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) { ++#ifndef __wasi__ + (*m->fMutex).~mutex(); + m->fMutex = nullptr; ++#endif + next = m->fListLink; + m->fListLink = nullptr; + } +@@ -110,20 +117,24 @@ void UMutex::cleanup() { + + U_CAPI void U_EXPORT2 + umtx_lock(UMutex *mutex) { ++#ifndef __wasi__ + if (mutex == nullptr) { + mutex = &globalMutex; + } + mutex->lock(); ++#endif + } + + + U_CAPI void U_EXPORT2 + umtx_unlock(UMutex* mutex) + { ++#ifndef __wasi__ + if (mutex == nullptr) { + mutex = &globalMutex; + } + mutex->unlock(); ++#endif + } + + +@@ -143,18 +154,22 @@ umtx_unlock(UMutex* mutex) + // + U_COMMON_API UBool U_EXPORT2 + umtx_initImplPreInit(UInitOnce &uio) { ++#ifndef __wasi__ + 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 { ++#ifndef __wasi__ + 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 +183,13 @@ umtx_initImplPreInit(UInitOnce &uio) { + + U_COMMON_API void U_EXPORT2 + umtx_initImplPostInit(UInitOnce &uio) { +- { ++#ifndef __wasi__ ++ { + std::unique_lock lock(*initMutex); + umtx_storeRelease(uio.fState, 2); + } + initCondition->notify_all(); ++#endif + } + + U_NAMESPACE_END +diff --git a/icu/icu4c/source/common/umutex.h b/icu/icu4c/source/common/umutex.h +index 8d76b3f3e6f59628779774cbb993307655b2729b..3f3ba1defc5b5d3e532bb4b592dd3d9cdc05b880 100644 +--- a/icu/icu4c/source/common/umutex.h ++++ b/icu/icu4c/source/common/umutex.h +@@ -20,9 +20,12 @@ + #ifndef UMUTEX_H + #define UMUTEX_H + ++#ifndef __wasi__ + #include + #include + #include ++#endif ++ + #include + + #include "unicode/utypes.h" +@@ -36,7 +39,7 @@ + // See issue ICU-20185. + #error U_USER_ATOMICS and U_USER_MUTEX_H are not supported + #endif +- ++#ifndef __wasi__ + // 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. +@@ -60,7 +63,7 @@ template struct std::atomic; + template struct std::atomic; + #endif + #endif +- ++#endif + + U_NAMESPACE_BEGIN + +@@ -69,7 +72,7 @@ U_NAMESPACE_BEGIN + * Low Level Atomic Operations, ICU wrappers for. + * + ****************************************************************************/ +- ++#ifndef __wasi__ + typedef std::atomic u_atomic_int32_t; + #define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) + +@@ -89,7 +92,28 @@ inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { + return var->fetch_sub(1) - 1; + } + ++#else ++ ++typedef int32_t u_atomic_int32_t; ++#define ATOMIC_INT32_T_INITIALIZER(val) val ++ ++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 + /************************************************************************************************* + * + * UInitOnce Definitions. +@@ -231,18 +255,25 @@ class U_COMMON_API UMutex { + + // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard + void lock() { ++#ifndef __wasi__ + std::mutex *m = fMutex.load(std::memory_order_acquire); + if (m == nullptr) { m = getMutex(); } + m->lock(); ++#endif ++ } ++ void unlock() { ++#ifndef __wasi__ ++ fMutex.load(std::memory_order_relaxed)->unlock(); ++#endif + } +- void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); } + + static void cleanup(); + + private: ++#ifndef __wasi__ + 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(). + */ +@@ -253,7 +284,9 @@ class U_COMMON_API UMutex { + * Initial fast check is inline, in lock(). The returned value may never + * be nullptr. + */ ++#ifndef __wasi__ + std::mutex *getMutex(); ++#endif + }; + + +diff --git a/icu/icu4c/source/common/unifiedcache.cpp b/icu/icu4c/source/common/unifiedcache.cpp +index 493ab79f6d9067e2e90e2dbf20dc41a6d24fae18..c5ca7ef97f018115aee85a09dfcee985a43ed453 100644 +--- a/icu/icu4c/source/common/unifiedcache.cpp ++++ b/icu/icu4c/source/common/unifiedcache.cpp +@@ -13,15 +13,19 @@ + #include "unifiedcache.h" + + #include // For std::max() ++#ifndef __wasi__ + #include ++#endif + + #include "uassert.h" + #include "uhash.h" + #include "ucln_cmn.h" + + static icu::UnifiedCache *gCache = NULL; ++#ifndef __wasi__ + static std::mutex *gCacheMutex = nullptr; + static std::condition_variable *gInProgressValueAddedCond; ++#endif + static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER; + + static const int32_t MAX_EVICT_ITERATIONS = 10; +@@ -34,10 +38,12 @@ static UBool U_CALLCONV unifiedcache_cleanup() { + gCacheInitOnce.reset(); + delete gCache; + gCache = nullptr; ++#ifndef __wasi__ + gCacheMutex->~mutex(); + gCacheMutex = nullptr; + gInProgressValueAddedCond->~condition_variable(); + gInProgressValueAddedCond = nullptr; ++#endif + return TRUE; + } + U_CDECL_END +@@ -71,9 +77,10 @@ static void U_CALLCONV cacheInit(UErrorCode &status) { + U_ASSERT(gCache == NULL); + ucln_common_registerCleanup( + UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup); +- ++#ifndef __wasi__ + gCacheMutex = STATIC_NEW(std::mutex); + gInProgressValueAddedCond = STATIC_NEW(std::condition_variable); ++#endif + gCache = new UnifiedCache(status); + if (gCache == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; +@@ -135,29 +142,38 @@ void UnifiedCache::setEvictionPolicy( + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + fMaxUnused = count; + fMaxPercentageOfInUse = percentageOfInUseItems; + } + + int32_t UnifiedCache::unusedCount() const { ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + return uhash_count(fHashtable) - fNumValuesInUse; + } + + int64_t UnifiedCache::autoEvictedCount() const { ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + return fAutoEvictedCount; + } + + int32_t UnifiedCache::keyCount() const { ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + return uhash_count(fHashtable); + } + + void UnifiedCache::flush() const { ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); +- ++#endif + // Use a loop in case cache items that are flushed held hard references to + // other cache items making those additional cache items eligible for + // flushing. +@@ -165,7 +181,9 @@ void UnifiedCache::flush() const { + } + + void UnifiedCache::handleUnreferencedObject() const { ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + --fNumValuesInUse; + _runEvictionSlice(); + } +@@ -184,7 +202,9 @@ void UnifiedCache::dump() { + } + + void UnifiedCache::dumpContents() const { ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + _dumpContents(); + } + +@@ -224,7 +244,9 @@ 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. ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + _flush(TRUE); + } + uhash_close(fHashtable); +@@ -325,7 +347,9 @@ void UnifiedCache::_putIfAbsentAndGet( + const CacheKeyBase &key, + const SharedObject *&value, + UErrorCode &status) const { ++#ifndef __wasi__ + std::lock_guard lock(*gCacheMutex); ++#endif + const UHashElement *element = uhash_find(fHashtable, &key); + if (element != NULL && !_inProgress(element)) { + _fetch(element, value, status); +@@ -350,14 +374,18 @@ UBool UnifiedCache::_poll( + UErrorCode &status) const { + U_ASSERT(value == NULL); + U_ASSERT(status == U_ZERO_ERROR); ++#ifndef __wasi__ + 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 != NULL && _inProgress(element)) { ++#ifndef __wasi__ + gInProgressValueAddedCond->wait(lock); ++#endif + element = uhash_find(fHashtable, &key); + } + +@@ -430,7 +458,9 @@ void UnifiedCache::_put( + + // Tell waiting threads that we replace in-progress status with + // an error. ++#ifndef __wasi__ + gInProgressValueAddedCond->notify_all(); ++#endif + } + + void UnifiedCache::_fetch( +diff --git a/icu/icu4c/source/i18n/decContext.h b/icu/icu4c/source/i18n/decContext.h +index 59ab65e59271ac769d50864a10cb66cfd9d8607e..20f3526be8101233327be451f8c9d78d9b6f3c8b 100644 +--- a/icu/icu4c/source/i18n/decContext.h ++++ b/icu/icu4c/source/i18n/decContext.h +@@ -61,7 +61,9 @@ + /* #include */ /* C99 standard integers */ + #endif + #include /* for printf, etc. */ ++#ifndef __wasi__ + #include /* for traps */ ++#endif + + /* Extended flags setting -- set this to 0 to use only IEEE flags */ + #if !defined(DECEXTFLAG) +diff --git a/icu/icu4c/source/i18n/decimfmt.cpp b/icu/icu4c/source/i18n/decimfmt.cpp +index daa1129a6ab1d70e9ee83c4f74f31fb640a3066f..9858ac901d841db5ff36be238f93226cadb58a74 100644 +--- a/icu/icu4c/source/i18n/decimfmt.cpp ++++ b/icu/icu4c/source/i18n/decimfmt.cpp +@@ -479,9 +479,13 @@ DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { + + DecimalFormat::~DecimalFormat() { + if (fields == nullptr) { return; } +- ++#ifndef __wasi__ + delete fields->atomicParser.exchange(nullptr); + delete fields->atomicCurrencyParser.exchange(nullptr); ++#else ++ delete fields->atomicParser; ++ delete fields->atomicCurrencyParser; ++#endif + delete fields; + } + +@@ -1626,8 +1630,13 @@ void DecimalFormat::touch(UErrorCode& status) { + setupFastFormat(); + + // Delete the parsers if they were made previously ++#ifndef __wasi__ + 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. + NumberFormat::setCurrency(fields->exportedProperties.currency.get(status).getISOCurrency(), status); +@@ -1662,7 +1671,11 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& sta + } + + // First try to get the pre-computed parser ++#ifndef __wasi__ + auto* ptr = fields->atomicParser.load(); ++#else ++ auto* ptr = fields->atomicParser; ++#endif + if (ptr != nullptr) { + return ptr; + } +@@ -1681,6 +1694,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); ++#ifndef __wasi__ + if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) { + // Another thread beat us to computing the parser + delete temp; +@@ -1689,13 +1703,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 ++#ifndef __wasi__ + auto* ptr = fields->atomicCurrencyParser.load(); ++#else ++ auto* ptr = fields->atomicCurrencyParser; ++#endif + if (ptr != nullptr) { + return ptr; + } +@@ -1710,6 +1732,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); ++#ifndef __wasi__ + if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) { + // Another thread beat us to computing the parser + delete temp; +@@ -1718,6 +1741,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/icu/icu4c/source/i18n/number_mapper.h b/icu/icu4c/source/i18n/number_mapper.h +index 9ecd776b3b4795512159c3e60c3b2c32cbb81c50..6f55d27e6c5d93a5bbdfb2aeef7e5b55732a173f 100644 +--- a/icu/icu4c/source/i18n/number_mapper.h ++++ b/icu/icu4c/source/i18n/number_mapper.h +@@ -7,7 +7,9 @@ + #ifndef __NUMBER_MAPPER_H__ + #define __NUMBER_MAPPER_H__ + ++#ifndef __wasi__ + #include ++#endif + #include "number_types.h" + #include "unicode/currpinf.h" + #include "standardplural.h" +@@ -193,11 +195,18 @@ struct DecimalFormatFields : public UMemory { + LocalizedNumberFormatter formatter; + + /** The lazy-computed parser for .parse() */ ++#ifndef __wasi__ + std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicParser = {}; ++#else ++ ::icu::numparse::impl::NumberParserImpl* atomicParser = nullptr; ++#endif + + /** The lazy-computed parser for .parseCurrency() */ ++#ifndef __wasi__ + 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/icu/icu4c/source/i18n/numrange_fluent.cpp b/icu/icu4c/source/i18n/numrange_fluent.cpp +index d9286d1d713d2cecc5d803ee4c181a1360c53c99..5568c0a51f4be55439481bd90cebb2373d0d72e6 100644 +--- a/icu/icu4c/source/i18n/numrange_fluent.cpp ++++ b/icu/icu4c/source/i18n/numrange_fluent.cpp +@@ -240,28 +240,49 @@ LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS&& src) U_N + : NFS(std::move(src)) { + // Steal the compiled formatter + LNF&& _src = static_cast(src); ++#ifndef __wasi__ + 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) { + NFS::operator=(static_cast&>(other)); + // Do not steal; just clear ++#ifndef __wasi__ + delete fAtomicFormatter.exchange(nullptr); ++#else ++ delete fAtomicFormatter; ++#endif + return *this; + } + + LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) U_NOEXCEPT { + NFS::operator=(static_cast&&>(src)); + // Steal the compiled formatter ++#ifndef __wasi__ + auto* stolen = src.fAtomicFormatter.exchange(nullptr); + delete fAtomicFormatter.exchange(stolen); ++#else ++ delete fAtomicFormatter; ++ fAtomicFormatter = src.fAtomicFormatter; ++ src.fAtomicFormatter = nullptr; ++#endif ++ + return *this; + } + + + LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() { ++#ifndef __wasi__ + delete fAtomicFormatter.exchange(nullptr); ++#else ++ delete fAtomicFormatter; ++#endif + } + + LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) { +@@ -345,7 +366,11 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { + } + + // First try to get the pre-computed formatter ++#ifndef __wasi__ + auto* ptr = fAtomicFormatter.load(); ++#else ++ auto* ptr = fAtomicFormatter; ++#endif + if (ptr != nullptr) { + return ptr; + } +@@ -364,6 +389,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); ++#ifndef __wasi__ + if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) { + // Another thread beat us to computing the formatter + delete temp; +@@ -372,6 +398,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/icu/icu4c/source/i18n/unicode/numberrangeformatter.h b/icu/icu4c/source/i18n/unicode/numberrangeformatter.h +index 4e0a15badb14b8b7dd907ff34ca0f8e427071546..52253908aee0a39bb940d92f26dffc3675cb918f 100644 +--- a/icu/icu4c/source/i18n/unicode/numberrangeformatter.h ++++ b/icu/icu4c/source/i18n/unicode/numberrangeformatter.h +@@ -9,8 +9,9 @@ + #if U_SHOW_CPLUSPLUS_API + + #if !UCONFIG_NO_FORMATTING +- ++#ifndef __wasi__ + #include ++#endif + #include "unicode/appendable.h" + #include "unicode/fieldpos.h" + #include "unicode/formattedvalue.h" +@@ -77,7 +78,9 @@ struct UFormattedNumberRangeImpl; + } // namespace icu::number + U_NAMESPACE_END + ++#ifndef __wasi__ + template struct U_I18N_API std::atomic< U_NAMESPACE_QUALIFIER number::impl::NumberRangeFormatterImpl*>; ++#endif + + U_NAMESPACE_BEGIN + namespace number { // icu::number +@@ -546,8 +549,11 @@ class U_I18N_API LocalizedNumberRangeFormatter + ~LocalizedNumberRangeFormatter(); + + private: ++#ifndef __wasi__ + std::atomic fAtomicFormatter = {}; +- ++#else ++ impl::NumberRangeFormatterImpl* fAtomicFormatter = nullptr; ++#endif + const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) const; + + explicit LocalizedNumberRangeFormatter( diff --git a/icu/icu4c/source/common/putilimp.h b/icu/icu4c/source/common/putilimp.h index a325c6c359a..ab5e8ac68ad 100644 --- a/icu/icu4c/source/common/putilimp.h +++ b/icu/icu4c/source/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/icu/icu4c/source/common/umapfile.h b/icu/icu4c/source/common/umapfile.h index 92bd567a2a9..4ed1112bc6d 100644 --- a/icu/icu4c/source/common/umapfile.h +++ b/icu/icu4c/source/common/umapfile.h @@ -41,6 +41,8 @@ U_CFUNC void uprv_unmapFile(UDataMemory *pData); #if UCONFIG_NO_FILE_IO # define MAP_IMPLEMENTATION MAP_NONE +#elif defined(__wasi__) +# define MAP_IMPLEMENTATION MAP_STDIO #elif U_PLATFORM_USES_ONLY_WIN32_API # define MAP_IMPLEMENTATION MAP_WIN32 #elif U_HAVE_MMAP || U_PLATFORM == U_PF_OS390 diff --git a/icu/icu4c/source/common/umutex.cpp b/icu/icu4c/source/common/umutex.cpp index ccbee9960a3..729644eed9c 100644 --- a/icu/icu4c/source/common/umutex.cpp +++ b/icu/icu4c/source/common/umutex.cpp @@ -42,7 +42,7 @@ U_NAMESPACE_BEGIN * ICU Mutex wrappers. * *************************************************************************************************/ - +#ifndef __wasi__ namespace { std::mutex *initMutex; std::condition_variable *initCondition; @@ -55,9 +55,11 @@ std::once_flag initFlag; std::once_flag *pInitFlag = &initFlag; } // Anonymous namespace +#endif U_CDECL_BEGIN static UBool U_CALLCONV umtx_cleanup() { +#ifndef __wasi__ initMutex->~mutex(); initCondition->~condition_variable(); UMutex::cleanup(); @@ -66,17 +68,20 @@ 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() { +#ifndef __wasi__ initMutex = STATIC_NEW(std::mutex); initCondition = STATIC_NEW(std::condition_variable); ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); +#endif } U_CDECL_END - +#ifndef __wasi__ std::mutex *UMutex::getMutex() { std::mutex *retPtr = fMutex.load(std::memory_order_acquire); if (retPtr == nullptr) { @@ -93,14 +98,16 @@ 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) { +#ifndef __wasi__ (*m->fMutex).~mutex(); m->fMutex = nullptr; +#endif next = m->fListLink; m->fListLink = nullptr; } @@ -110,20 +117,24 @@ void UMutex::cleanup() { U_CAPI void U_EXPORT2 umtx_lock(UMutex *mutex) { +#ifndef __wasi__ if (mutex == nullptr) { mutex = &globalMutex; } mutex->lock(); +#endif } U_CAPI void U_EXPORT2 umtx_unlock(UMutex* mutex) { +#ifndef __wasi__ if (mutex == nullptr) { mutex = &globalMutex; } mutex->unlock(); +#endif } @@ -143,18 +154,22 @@ umtx_unlock(UMutex* mutex) // U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { +#ifndef __wasi__ 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 { +#ifndef __wasi__ 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 +183,13 @@ umtx_initImplPreInit(UInitOnce &uio) { U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { - { +#ifndef __wasi__ + { std::unique_lock lock(*initMutex); umtx_storeRelease(uio.fState, 2); } initCondition->notify_all(); +#endif } U_NAMESPACE_END diff --git a/icu/icu4c/source/common/umutex.h b/icu/icu4c/source/common/umutex.h index 8d76b3f3e6f..3f3ba1defc5 100644 --- a/icu/icu4c/source/common/umutex.h +++ b/icu/icu4c/source/common/umutex.h @@ -20,9 +20,12 @@ #ifndef UMUTEX_H #define UMUTEX_H +#ifndef __wasi__ #include #include #include +#endif + #include #include "unicode/utypes.h" @@ -36,7 +39,7 @@ // See issue ICU-20185. #error U_USER_ATOMICS and U_USER_MUTEX_H are not supported #endif - +#ifndef __wasi__ // 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. @@ -60,7 +63,7 @@ template struct std::atomic; template struct std::atomic; #endif #endif - +#endif U_NAMESPACE_BEGIN @@ -69,7 +72,7 @@ U_NAMESPACE_BEGIN * Low Level Atomic Operations, ICU wrappers for. * ****************************************************************************/ - +#ifndef __wasi__ typedef std::atomic u_atomic_int32_t; #define ATOMIC_INT32_T_INITIALIZER(val) ATOMIC_VAR_INIT(val) @@ -89,7 +92,28 @@ inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) { return var->fetch_sub(1) - 1; } +#else + +typedef int32_t u_atomic_int32_t; +#define ATOMIC_INT32_T_INITIALIZER(val) val + +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 /************************************************************************************************* * * UInitOnce Definitions. @@ -231,18 +255,25 @@ class U_COMMON_API UMutex { // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard void lock() { +#ifndef __wasi__ std::mutex *m = fMutex.load(std::memory_order_acquire); if (m == nullptr) { m = getMutex(); } m->lock(); +#endif + } + void unlock() { +#ifndef __wasi__ + fMutex.load(std::memory_order_relaxed)->unlock(); +#endif } - void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); } static void cleanup(); private: +#ifndef __wasi__ 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(). */ @@ -253,7 +284,9 @@ class U_COMMON_API UMutex { * Initial fast check is inline, in lock(). The returned value may never * be nullptr. */ +#ifndef __wasi__ std::mutex *getMutex(); +#endif }; diff --git a/icu/icu4c/source/common/unifiedcache.cpp b/icu/icu4c/source/common/unifiedcache.cpp index 493ab79f6d9..c5ca7ef97f0 100644 --- a/icu/icu4c/source/common/unifiedcache.cpp +++ b/icu/icu4c/source/common/unifiedcache.cpp @@ -13,15 +13,19 @@ #include "unifiedcache.h" #include // For std::max() +#ifndef __wasi__ #include +#endif #include "uassert.h" #include "uhash.h" #include "ucln_cmn.h" static icu::UnifiedCache *gCache = NULL; +#ifndef __wasi__ static std::mutex *gCacheMutex = nullptr; static std::condition_variable *gInProgressValueAddedCond; +#endif static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER; static const int32_t MAX_EVICT_ITERATIONS = 10; @@ -34,10 +38,12 @@ static UBool U_CALLCONV unifiedcache_cleanup() { gCacheInitOnce.reset(); delete gCache; gCache = nullptr; +#ifndef __wasi__ gCacheMutex->~mutex(); gCacheMutex = nullptr; gInProgressValueAddedCond->~condition_variable(); gInProgressValueAddedCond = nullptr; +#endif return TRUE; } U_CDECL_END @@ -71,9 +77,10 @@ static void U_CALLCONV cacheInit(UErrorCode &status) { U_ASSERT(gCache == NULL); ucln_common_registerCleanup( UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup); - +#ifndef __wasi__ gCacheMutex = STATIC_NEW(std::mutex); gInProgressValueAddedCond = STATIC_NEW(std::condition_variable); +#endif gCache = new UnifiedCache(status); if (gCache == NULL) { status = U_MEMORY_ALLOCATION_ERROR; @@ -135,29 +142,38 @@ void UnifiedCache::setEvictionPolicy( status = U_ILLEGAL_ARGUMENT_ERROR; return; } +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif fMaxUnused = count; fMaxPercentageOfInUse = percentageOfInUseItems; } int32_t UnifiedCache::unusedCount() const { +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif return uhash_count(fHashtable) - fNumValuesInUse; } int64_t UnifiedCache::autoEvictedCount() const { +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif return fAutoEvictedCount; } int32_t UnifiedCache::keyCount() const { +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif return uhash_count(fHashtable); } void UnifiedCache::flush() const { +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); - +#endif // Use a loop in case cache items that are flushed held hard references to // other cache items making those additional cache items eligible for // flushing. @@ -165,7 +181,9 @@ void UnifiedCache::flush() const { } void UnifiedCache::handleUnreferencedObject() const { +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif --fNumValuesInUse; _runEvictionSlice(); } @@ -184,7 +202,9 @@ void UnifiedCache::dump() { } void UnifiedCache::dumpContents() const { +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif _dumpContents(); } @@ -224,7 +244,9 @@ 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. +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif _flush(TRUE); } uhash_close(fHashtable); @@ -325,7 +347,9 @@ void UnifiedCache::_putIfAbsentAndGet( const CacheKeyBase &key, const SharedObject *&value, UErrorCode &status) const { +#ifndef __wasi__ std::lock_guard lock(*gCacheMutex); +#endif const UHashElement *element = uhash_find(fHashtable, &key); if (element != NULL && !_inProgress(element)) { _fetch(element, value, status); @@ -350,14 +374,18 @@ UBool UnifiedCache::_poll( UErrorCode &status) const { U_ASSERT(value == NULL); U_ASSERT(status == U_ZERO_ERROR); +#ifndef __wasi__ 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 != NULL && _inProgress(element)) { +#ifndef __wasi__ gInProgressValueAddedCond->wait(lock); +#endif element = uhash_find(fHashtable, &key); } @@ -430,7 +458,9 @@ void UnifiedCache::_put( // Tell waiting threads that we replace in-progress status with // an error. +#ifndef __wasi__ gInProgressValueAddedCond->notify_all(); +#endif } void UnifiedCache::_fetch( diff --git a/icu/icu4c/source/i18n/decContext.h b/icu/icu4c/source/i18n/decContext.h index 59ab65e5927..20f3526be81 100644 --- a/icu/icu4c/source/i18n/decContext.h +++ b/icu/icu4c/source/i18n/decContext.h @@ -61,7 +61,9 @@ /* #include */ /* C99 standard integers */ #endif #include /* for printf, etc. */ +#ifndef __wasi__ #include /* for traps */ +#endif /* Extended flags setting -- set this to 0 to use only IEEE flags */ #if !defined(DECEXTFLAG) diff --git a/icu/icu4c/source/i18n/decimfmt.cpp b/icu/icu4c/source/i18n/decimfmt.cpp index daa1129a6ab..9858ac901d8 100644 --- a/icu/icu4c/source/i18n/decimfmt.cpp +++ b/icu/icu4c/source/i18n/decimfmt.cpp @@ -479,9 +479,13 @@ DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { DecimalFormat::~DecimalFormat() { if (fields == nullptr) { return; } - +#ifndef __wasi__ delete fields->atomicParser.exchange(nullptr); delete fields->atomicCurrencyParser.exchange(nullptr); +#else + delete fields->atomicParser; + delete fields->atomicCurrencyParser; +#endif delete fields; } @@ -1626,8 +1630,13 @@ void DecimalFormat::touch(UErrorCode& status) { setupFastFormat(); // Delete the parsers if they were made previously +#ifndef __wasi__ 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. NumberFormat::setCurrency(fields->exportedProperties.currency.get(status).getISOCurrency(), status); @@ -1662,7 +1671,11 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& sta } // First try to get the pre-computed parser +#ifndef __wasi__ auto* ptr = fields->atomicParser.load(); +#else + auto* ptr = fields->atomicParser; +#endif if (ptr != nullptr) { return ptr; } @@ -1681,6 +1694,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); +#ifndef __wasi__ if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the parser delete temp; @@ -1689,13 +1703,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 +#ifndef __wasi__ auto* ptr = fields->atomicCurrencyParser.load(); +#else + auto* ptr = fields->atomicCurrencyParser; +#endif if (ptr != nullptr) { return ptr; } @@ -1710,6 +1732,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); +#ifndef __wasi__ if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the parser delete temp; @@ -1718,6 +1741,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/icu/icu4c/source/i18n/number_mapper.h b/icu/icu4c/source/i18n/number_mapper.h index 9ecd776b3b4..6f55d27e6c5 100644 --- a/icu/icu4c/source/i18n/number_mapper.h +++ b/icu/icu4c/source/i18n/number_mapper.h @@ -7,7 +7,9 @@ #ifndef __NUMBER_MAPPER_H__ #define __NUMBER_MAPPER_H__ +#ifndef __wasi__ #include +#endif #include "number_types.h" #include "unicode/currpinf.h" #include "standardplural.h" @@ -193,11 +195,18 @@ struct DecimalFormatFields : public UMemory { LocalizedNumberFormatter formatter; /** The lazy-computed parser for .parse() */ +#ifndef __wasi__ std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicParser = {}; +#else + ::icu::numparse::impl::NumberParserImpl* atomicParser = nullptr; +#endif /** The lazy-computed parser for .parseCurrency() */ +#ifndef __wasi__ 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/icu/icu4c/source/i18n/numrange_fluent.cpp b/icu/icu4c/source/i18n/numrange_fluent.cpp index d9286d1d713..5568c0a51f4 100644 --- a/icu/icu4c/source/i18n/numrange_fluent.cpp +++ b/icu/icu4c/source/i18n/numrange_fluent.cpp @@ -240,28 +240,49 @@ LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS&& src) U_N : NFS(std::move(src)) { // Steal the compiled formatter LNF&& _src = static_cast(src); +#ifndef __wasi__ 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) { NFS::operator=(static_cast&>(other)); // Do not steal; just clear +#ifndef __wasi__ delete fAtomicFormatter.exchange(nullptr); +#else + delete fAtomicFormatter; +#endif return *this; } LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) U_NOEXCEPT { NFS::operator=(static_cast&&>(src)); // Steal the compiled formatter +#ifndef __wasi__ auto* stolen = src.fAtomicFormatter.exchange(nullptr); delete fAtomicFormatter.exchange(stolen); +#else + delete fAtomicFormatter; + fAtomicFormatter = src.fAtomicFormatter; + src.fAtomicFormatter = nullptr; +#endif + return *this; } LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() { +#ifndef __wasi__ delete fAtomicFormatter.exchange(nullptr); +#else + delete fAtomicFormatter; +#endif } LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) { @@ -345,7 +366,11 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const { } // First try to get the pre-computed formatter +#ifndef __wasi__ auto* ptr = fAtomicFormatter.load(); +#else + auto* ptr = fAtomicFormatter; +#endif if (ptr != nullptr) { return ptr; } @@ -364,6 +389,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); +#ifndef __wasi__ if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the formatter delete temp; @@ -372,6 +398,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/icu/icu4c/source/i18n/unicode/numberrangeformatter.h b/icu/icu4c/source/i18n/unicode/numberrangeformatter.h index 4e0a15badb1..52253908aee 100644 --- a/icu/icu4c/source/i18n/unicode/numberrangeformatter.h +++ b/icu/icu4c/source/i18n/unicode/numberrangeformatter.h @@ -9,8 +9,9 @@ #if U_SHOW_CPLUSPLUS_API #if !UCONFIG_NO_FORMATTING - +#ifndef __wasi__ #include +#endif #include "unicode/appendable.h" #include "unicode/fieldpos.h" #include "unicode/formattedvalue.h" @@ -77,7 +78,9 @@ struct UFormattedNumberRangeImpl; } // namespace icu::number U_NAMESPACE_END +#ifndef __wasi__ template struct U_I18N_API std::atomic< U_NAMESPACE_QUALIFIER number::impl::NumberRangeFormatterImpl*>; +#endif U_NAMESPACE_BEGIN namespace number { // icu::number @@ -546,8 +549,11 @@ class U_I18N_API LocalizedNumberRangeFormatter ~LocalizedNumberRangeFormatter(); private: +#ifndef __wasi__ std::atomic fAtomicFormatter = {}; - +#else + impl::NumberRangeFormatterImpl* fAtomicFormatter = nullptr; +#endif const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) const; explicit LocalizedNumberRangeFormatter(