Source code
Revision control
Copy as Markdown
Other Tools
# Handle WASI lack of support for <thread> and <atomic>.
#
diff --git a/intl/icu/source/common/putilimp.h b/intl/icu/source/common/putilimp.h
index 5b95a68..7097232 100644
--- a/intl/icu/source/common/putilimp.h
+++ b/intl/icu/source/common/putilimp.h
@@ -105,10 +105,12 @@ typedef size_t uintptr_t;
 #endif
 #elif U_PLATFORM == U_PF_OS400
    /* not defined */
 #elif U_PLATFORM == U_PF_HAIKU
    /* not defined */
+#elif defined(__wasi__)
+   /* not defined */
 #else
 #   define U_TZSET tzset
 #endif
 
 #if defined(U_TIMEZONE) || defined(U_HAVE_TIMEZONE)
@@ -130,10 +132,12 @@ typedef size_t uintptr_t;
    /* not defined */
 #elif U_PLATFORM == U_PF_OS400
    /* not defined */
 #elif U_PLATFORM == U_PF_IPHONE
    /* not defined */
+#elif defined(__wasi__)
+   /* not defined */
 #else
 #   define U_TIMEZONE timezone
 #endif
 
 #if defined(U_TZNAME) || defined(U_HAVE_TZNAME)
@@ -145,10 +149,12 @@ typedef size_t uintptr_t;
 #endif
 #elif U_PLATFORM == U_PF_OS400
    /* not defined */
 #elif U_PLATFORM == U_PF_HAIKU
     /* not defined, (well it is but a loop back to icu) */
+#elif defined(__wasi__)
+   /* not defined */
 #else
 #   define U_TZNAME tzname
 #endif
 
 #ifdef U_HAVE_MMAP
diff --git a/intl/icu/source/common/umapfile.h b/intl/icu/source/common/umapfile.h
index 92bd567..4ed1112 100644
--- a/intl/icu/source/common/umapfile.h
+++ b/intl/icu/source/common/umapfile.h
@@ -38,10 +38,12 @@ U_CFUNC void  uprv_unmapFile(UDataMemory *pData);
 #define MAP_POSIX       2
 #define MAP_STDIO       3
 
 #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
 #   define MAP_IMPLEMENTATION MAP_POSIX
 #else /* unknown platform, no memory map implementation: use stdio.h and uprv_malloc() instead */
diff --git a/intl/icu/source/common/umutex.cpp b/intl/icu/source/common/umutex.cpp
index ccbee99..6c3452c 100644
--- a/intl/icu/source/common/umutex.cpp
+++ b/intl/icu/source/common/umutex.cpp
@@ -43,6 +43,7 @@ U_NAMESPACE_BEGIN
  *
  *************************************************************************************************/
 
+#ifndef __wasi__
 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() {
+#ifndef __wasi__
     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() {
+#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 +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) {
+#ifndef __wasi__
         (*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) {
+#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 +157,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<std::mutex> 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 +186,13 @@ umtx_initImplPreInit(UInitOnce &uio) {
 
 U_COMMON_API void U_EXPORT2
 umtx_initImplPostInit(UInitOnce &uio) {
+#ifndef __wasi__
     {
         std::unique_lock<std::mutex> lock(*initMutex);
         umtx_storeRelease(uio.fState, 2);
     }
     initCondition->notify_all();
+#endif
 }
 
 U_NAMESPACE_END
diff --git a/intl/icu/source/common/umutex.h b/intl/icu/source/common/umutex.h
index 8d76b3f..c1a58db 100644
--- a/intl/icu/source/common/umutex.h
+++ b/intl/icu/source/common/umutex.h
@@ -20,9 +20,12 @@
 #ifndef UMUTEX_H
 #define UMUTEX_H
 
+#ifndef __wasi__
 #include <atomic>
 #include <condition_variable>
 #include <mutex>
+#endif
+
 #include <type_traits>
 
 #include "unicode/utypes.h"
@@ -37,6 +40,8 @@
 #error U_USER_ATOMICS and U_USER_MUTEX_H are not supported
 #endif
 
+#ifndef __wasi__
+
 // Export an explicit template instantiation of std::atomic<int32_t>. 
 // 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 +66,7 @@ template struct std::atomic<std::mutex *>;
 #endif
 #endif
 
+#endif
 
 U_NAMESPACE_BEGIN
 
@@ -68,10 +68,12 @@ U_NAMESPACE_BEGIN
  *
  *   Low Level Atomic Operations, ICU wrappers for.
  *
  ****************************************************************************/
 
+#ifndef __wasi__
+
 typedef std::atomic<int32_t> u_atomic_int32_t;
 
 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
     return var.load(std::memory_order_acquire);
 }
@@ -86,10 +88,31 @@ inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
 
 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;
+
+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.
  *
@@ -225,21 +248,29 @@ class U_COMMON_API UMutex {
     UMutex &operator =(const UMutex &other) = delete;
     void *operator new(size_t) = delete;
 
     // 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<std::mutex *> 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().
      */
     UMutex *fListLink { nullptr };
@@ -247,11 +278,13 @@ class U_COMMON_API UMutex {
 
     /** Out-of-line function to lazily initialize a UMutex on first use.
      * Initial fast check is inline, in lock().  The returned value may never
      * be nullptr.
      */
+#ifndef __wasi__
     std::mutex *getMutex();
+#endif
 };
 
 
 /* Lock a mutex.
  * @param mutex The given mutex to be locked.  Pass NULL to specify
diff --git a/intl/icu/source/common/unifiedcache.cpp b/intl/icu/source/common/unifiedcache.cpp
--- a/intl/icu/source/common/unifiedcache.cpp
+++ b/intl/icu/source/common/unifiedcache.cpp
@@ -11,19 +11,23 @@
 */
 
 #include "unifiedcache.h"
 
 #include <algorithm>      // For std::max()
-#include <mutex>
+#ifndef __wasi__
+ #include <mutex>
+#endif
 
 #include "uassert.h"
 #include "uhash.h"
 #include "ucln_cmn.h"
 
 static icu::UnifiedCache *gCache = nullptr;
+#ifndef __wasi__
 static std::mutex *gCacheMutex = nullptr;
 static std::condition_variable *gInProgressValueAddedCond;
+#endif
 static icu::UInitOnce gCacheInitOnce {};
 
 static const int32_t MAX_EVICT_ITERATIONS = 10;
 static const int32_t DEFAULT_MAX_UNUSED = 1000;
 static const int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100;
@@ -32,14 +36,16 @@ static const int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100;
 U_CDECL_BEGIN
 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
 
 
@@ -70,12 +76,14 @@ CacheKeyBase::~CacheKeyBase() {
 static void U_CALLCONV cacheInit(UErrorCode &status) {
     U_ASSERT(gCache == nullptr);
     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 == nullptr) {
         status = U_MEMORY_ALLOCATION_ERROR;
     }
     if (U_FAILURE(status)) {
@@ -133,41 +141,53 @@ void UnifiedCache::setEvictionPolicy(
     }
     if (count < 0 || percentageOfInUseItems < 0) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
         return;
     }
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     fMaxUnused = count;
     fMaxPercentageOfInUse = percentageOfInUseItems;
 }
 
 int32_t UnifiedCache::unusedCount() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     return uhash_count(fHashtable) - fNumValuesInUse;
 }
 
 int64_t UnifiedCache::autoEvictedCount() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     return fAutoEvictedCount;
 }
 
 int32_t UnifiedCache::keyCount() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     return uhash_count(fHashtable);
 }
 
 void UnifiedCache::flush() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> 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.
     while (_flush(false));
 }
 
 void UnifiedCache::handleUnreferencedObject() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     --fNumValuesInUse;
     _runEvictionSlice();
 }
 
 #ifdef UNIFIED_CACHE_DEBUG
@@ -182,11 +202,13 @@ void UnifiedCache::dump() {
     }
     cache->dumpContents();
 }
 
 void UnifiedCache::dumpContents() const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     _dumpContents();
 }
 
 // Dumps content of cache.
 // On entry, gCacheMutex must be held.
@@ -222,11 +244,13 @@ UnifiedCache::~UnifiedCache() {
     flush();
     {
         // 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<std::mutex> lock(*gCacheMutex);
+#endif
         _flush(true);
     }
     uhash_close(fHashtable);
     fHashtable = nullptr;
     delete fNoValue;
@@ -323,11 +347,13 @@ void UnifiedCache::_putNew(
 
 void UnifiedCache::_putIfAbsentAndGet(
         const CacheKeyBase &key,
         const SharedObject *&value,
         UErrorCode &status) const {
+#ifndef __wasi__
     std::lock_guard<std::mutex> lock(*gCacheMutex);
+#endif
     const UHashElement *element = uhash_find(fHashtable, &key);
     if (element != nullptr && !_inProgress(element)) {
         _fetch(element, value, status);
         return;
     }
@@ -348,18 +374,22 @@ UBool UnifiedCache::_poll(
         const CacheKeyBase &key,
         const SharedObject *&value,
         UErrorCode &status) const {
     U_ASSERT(value == nullptr);
     U_ASSERT(status == U_ZERO_ERROR);
+#ifndef __wasi__
     std::unique_lock<std::mutex> 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)) {
+#ifndef __wasi__
          gInProgressValueAddedCond->wait(lock);
+#endif
          element = uhash_find(fHashtable, &key);
     }
 
     // If the hash table contains an entry for the key,
     // fetch out the contents and return them.
@@ -426,13 +456,15 @@ void UnifiedCache::_put(
     UHashElement *ptr = const_cast<UHashElement *>(element);
     ptr->value.pointer = (void *) value;
     U_ASSERT(oldValue == fNoValue);
     removeSoftRef(oldValue);
 
+#ifndef __wasi__
     // Tell waiting threads that we replace in-progress status with
     // an error.
     gInProgressValueAddedCond->notify_all();
+#endif
 }
 
 void UnifiedCache::_fetch(
         const UHashElement *element,
         const SharedObject *&value,
diff --git a/intl/icu/source/i18n/decContext.h b/intl/icu/source/i18n/decContext.h
index 59ab65e..20f3526 100644
--- a/intl/icu/source/i18n/decContext.h
+++ b/intl/icu/source/i18n/decContext.h
@@ -61,7 +61,9 @@
 /* #include <stdint.h>   */         /* C99 standard integers           */
   #endif
   #include <stdio.h>               /* for printf, etc.                */
+#ifndef __wasi__
   #include <signal.h>              /* for traps                       */
+#endif
 
   /* Extended flags setting -- set this to 0 to use only IEEE flags   */
   #if !defined(DECEXTFLAG)
diff --git a/intl/icu/source/i18n/decimfmt.cpp b/intl/icu/source/i18n/decimfmt.cpp
index daa1129..c8f1eda 100644
--- a/intl/icu/source/i18n/decimfmt.cpp
+++ b/intl/icu/source/i18n/decimfmt.cpp
@@ -480,8 +480,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 +1631,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 +1672,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 +1695,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<DecimalFormat*>(this);
+#ifndef __wasi__
     if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) {
         // Another thread beat us to computing the parser
         delete temp;
@@ -1689,13 +1704,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 +1733,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<DecimalFormat*>(this);
+#ifndef __wasi__
     if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) {
         // Another thread beat us to computing the parser
         delete temp;
@@ -1718,6 +1742,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/intl/icu/source/i18n/number_mapper.h b/intl/icu/source/i18n/number_mapper.h
index 9ecd776..d094289 100644
--- a/intl/icu/source/i18n/number_mapper.h
+++ b/intl/icu/source/i18n/number_mapper.h
@@ -7,7 +7,6 @@
 #ifndef __NUMBER_MAPPER_H__
 #define __NUMBER_MAPPER_H__
 
-#include <atomic>
 #include "number_types.h"
 #include "unicode/currpinf.h"
 #include "standardplural.h"
@@ -15,6 +14,10 @@
 #include "number_currencysymbols.h"
 #include "numparse_impl.h"
 
+#ifndef __wasi__
+#include <atomic>
+#endif
+
 U_NAMESPACE_BEGIN
 namespace number {
 namespace impl {
@@ -193,10 +196,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/intl/icu/source/i18n/numrange_fluent.cpp b/intl/icu/source/i18n/numrange_fluent.cpp
--- a/intl/icu/source/i18n/numrange_fluent.cpp
+++ b/intl/icu/source/i18n/numrange_fluent.cpp
@@ -246,33 +246,53 @@ LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRang
 
 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) noexcept
         : NFS<LNF>(std::move(src)) {
     // Steal the compiled formatter
     LNF&& _src = static_cast<LNF&&>(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) {
     if (this == &other) { return *this; }  // self-assignment: no-op
     NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
     // Do not steal; just clear
+#ifndef __wasi__
     delete fAtomicFormatter.exchange(nullptr);
+#else
+    delete fAtomicFormatter;
+#endif
     return *this;
 }
 
 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) noexcept {
     NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(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) {
     fMacros = macros;
     fMacros.locale = locale;
@@ -363,11 +383,15 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const {
     if (U_FAILURE(status)) {
         return nullptr;
     }
 
     // First try to get the pre-computed formatter
+#ifndef __wasi__
     auto* ptr = fAtomicFormatter.load();
+#else
+    auto* ptr = fAtomicFormatter;
+#endif
     if (ptr != nullptr) {
         return ptr;
     }
 
     // Try computing the formatter on our own
@@ -378,17 +378,22 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const {
 
     // 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 formatter object.
     auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
+#ifndef __wasi__
     if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp.getAlias())) {
         // Another thread beat us to computing the formatter
         return ptr;
     } else {
         // Our copy of the formatter got stored in the atomic
         return temp.orphan();
     }
+#else
+    nonConstThis->fAtomicFormatter = temp.getAlias();
+    return temp.orphan();
+#endif
 
 }
 
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/intl/icu/source/i18n/unicode/numberrangeformatter.h b/intl/icu/source/i18n/unicode/numberrangeformatter.h
index b9a4600..0ba2fa0 100644
--- a/intl/icu/source/i18n/unicode/numberrangeformatter.h
+++ b/intl/icu/source/i18n/unicode/numberrangeformatter.h
@@ -10,7 +10,6 @@
 
 #if !UCONFIG_NO_FORMATTING
 
-#include <atomic>
 #include "unicode/appendable.h"
 #include "unicode/fieldpos.h"
 #include "unicode/formattedvalue.h"
@@ -18,6 +17,10 @@
 #include "unicode/numberformatter.h"
 #include "unicode/unumberrangeformatter.h"
 
+#ifndef __wasi__
+#include <atomic>
+#endif
+
 /**
  * \file
  * \brief C++ API: Library for localized formatting of number, currency, and unit ranges.
@@ -77,7 +80,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,7 +551,11 @@ class U_I18N_API LocalizedNumberRangeFormatter
     ~LocalizedNumberRangeFormatter();
 
   private:
+#ifndef __wasi__
     std::atomic<impl::NumberRangeFormatterImpl*> fAtomicFormatter = {};
+#else
+    impl::NumberRangeFormatterImpl* fAtomicFormatter = nullptr;
+#endif
 
     const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) const;