tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

bug-1706949-wasi-workaround.diff (25019B)


      1 # Handle WASI lack of support for <thread> and <atomic>.
      2 #
      3 # WASI issue: https://github.com/WebAssembly/wasi-sdk/issues/180
      4 
      5 diff --git a/intl/icu/source/common/putilimp.h b/intl/icu/source/common/putilimp.h
      6 --- a/intl/icu/source/common/putilimp.h
      7 +++ b/intl/icu/source/common/putilimp.h
      8 @@ -105,10 +105,12 @@ typedef size_t uintptr_t;
      9 #endif
     10 #elif U_PLATFORM == U_PF_OS400
     11    /* not defined */
     12 #elif U_PLATFORM == U_PF_HAIKU
     13    /* not defined */
     14 +#elif defined(__wasi__)
     15 +   /* not defined */
     16 #else
     17 #   define U_TZSET tzset
     18 #endif
     19 
     20 #if defined(U_TIMEZONE) || defined(U_HAVE_TIMEZONE)
     21 @@ -130,10 +132,12 @@ typedef size_t uintptr_t;
     22    /* not defined */
     23 #elif U_PLATFORM == U_PF_OS400
     24    /* not defined */
     25 #elif U_PLATFORM == U_PF_IPHONE
     26    /* not defined */
     27 +#elif defined(__wasi__)
     28 +   /* not defined */
     29 #else
     30 #   define U_TIMEZONE timezone
     31 #endif
     32 
     33 #if defined(U_TZNAME) || defined(U_HAVE_TZNAME)
     34 @@ -145,10 +149,12 @@ typedef size_t uintptr_t;
     35 #endif
     36 #elif U_PLATFORM == U_PF_OS400
     37    /* not defined */
     38 #elif U_PLATFORM == U_PF_HAIKU
     39     /* not defined, (well it is but a loop back to icu) */
     40 +#elif defined(__wasi__)
     41 +   /* not defined */
     42 #else
     43 #   define U_TZNAME tzname
     44 #endif
     45 
     46 #ifdef U_HAVE_MMAP
     47 diff --git a/intl/icu/source/common/umapfile.h b/intl/icu/source/common/umapfile.h
     48 --- a/intl/icu/source/common/umapfile.h
     49 +++ b/intl/icu/source/common/umapfile.h
     50 @@ -38,10 +38,12 @@ U_CFUNC void  uprv_unmapFile(UDataMemory
     51 #define MAP_POSIX       2
     52 #define MAP_STDIO       3
     53 
     54 #if UCONFIG_NO_FILE_IO
     55 #   define MAP_IMPLEMENTATION MAP_NONE
     56 +#elif defined(__wasi__)
     57 +#   define MAP_IMPLEMENTATION MAP_STDIO
     58 #elif U_PLATFORM_USES_ONLY_WIN32_API
     59 #   define MAP_IMPLEMENTATION MAP_WIN32
     60 #elif U_HAVE_MMAP || U_PLATFORM == U_PF_OS390
     61 #   define MAP_IMPLEMENTATION MAP_POSIX
     62 #else /* unknown platform, no memory map implementation: use stdio.h and uprv_malloc() instead */
     63 diff --git a/intl/icu/source/common/umutex.cpp b/intl/icu/source/common/umutex.cpp
     64 --- a/intl/icu/source/common/umutex.cpp
     65 +++ b/intl/icu/source/common/umutex.cpp
     66 @@ -41,10 +41,11 @@ U_NAMESPACE_BEGIN
     67  *
     68  *  ICU Mutex wrappers.
     69  *
     70  *************************************************************************************************/
     71 
     72 +#ifndef __wasi__
     73 namespace {
     74 std::mutex *initMutex;
     75 std::condition_variable *initCondition;
     76 
     77 // The ICU global mutex.
     78 @@ -53,32 +54,38 @@ UMutex globalMutex;
     79 
     80 std::once_flag initFlag;
     81 std::once_flag *pInitFlag = &initFlag;
     82 
     83 }  // Anonymous namespace
     84 +#endif
     85 
     86 U_CDECL_BEGIN
     87 static UBool U_CALLCONV umtx_cleanup() {
     88 +#ifndef __wasi__
     89     initMutex->~mutex();
     90     initCondition->~condition_variable();
     91     UMutex::cleanup();
     92 
     93     // Reset the once_flag, by destructing it and creating a fresh one in its place.
     94     // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once().
     95     pInitFlag->~once_flag();
     96     pInitFlag = new(&initFlag) std::once_flag();
     97 +#endif
     98     return true;
     99 }
    100 
    101 static void U_CALLCONV umtx_init() {
    102 +#ifndef __wasi__
    103     initMutex = STATIC_NEW(std::mutex);
    104     initCondition = STATIC_NEW(std::condition_variable);
    105     ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
    106 +#endif
    107 }
    108 U_CDECL_END
    109 
    110 
    111 +#ifndef __wasi__
    112 std::mutex *UMutex::getMutex() {
    113     std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
    114     if (retPtr == nullptr) {
    115         std::call_once(*pInitFlag, umtx_init);
    116         std::lock_guard<std::mutex> guard(*initMutex);
    117 @@ -91,41 +98,48 @@ std::mutex *UMutex::getMutex() {
    118         }
    119     }
    120     U_ASSERT(retPtr != nullptr);
    121     return retPtr;
    122 }
    123 +#endif
    124 
    125 UMutex *UMutex::gListHead = nullptr;
    126 
    127 void UMutex::cleanup() {
    128     UMutex *next = nullptr;
    129     for (UMutex *m = gListHead; m != nullptr; m = next) {
    130 +#ifndef __wasi__
    131         (*m->fMutex).~mutex();
    132         m->fMutex = nullptr;
    133 +#endif
    134         next = m->fListLink;
    135         m->fListLink = nullptr;
    136     }
    137     gListHead = nullptr;
    138 }
    139 
    140 
    141 U_CAPI void  U_EXPORT2
    142 umtx_lock(UMutex *mutex) {
    143 +#ifndef __wasi__
    144     if (mutex == nullptr) {
    145         mutex = &globalMutex;
    146     }
    147     mutex->lock();
    148 +#endif
    149 }
    150 
    151 
    152 U_CAPI void  U_EXPORT2
    153 umtx_unlock(UMutex* mutex)
    154 {
    155 +#ifndef __wasi__
    156     if (mutex == nullptr) {
    157         mutex = &globalMutex;
    158     }
    159     mutex->unlock();
    160 +#endif
    161 }
    162 
    163 
    164 /*************************************************************************************************
    165  *
    166 @@ -141,22 +155,26 @@ umtx_unlock(UMutex* mutex)
    167 //   that knows the C++ types involved. This function returns true if
    168 //   the caller needs to call the Init function.
    169 //
    170 U_COMMON_API UBool U_EXPORT2
    171 umtx_initImplPreInit(UInitOnce &uio) {
    172 +#ifndef __wasi__
    173     std::call_once(*pInitFlag, umtx_init);
    174     std::unique_lock<std::mutex> lock(*initMutex);
    175 +#endif
    176     if (umtx_loadAcquire(uio.fState) == 0) {
    177         umtx_storeRelease(uio.fState, 1);
    178         return true;      // Caller will next call the init function.
    179     } else {
    180 +#ifndef __wasi__
    181         while (umtx_loadAcquire(uio.fState) == 1) {
    182             // Another thread is currently running the initialization.
    183             // Wait until it completes.
    184             initCondition->wait(lock);
    185         }
    186         U_ASSERT(uio.fState == 2);
    187 +#endif
    188         return false;
    189     }
    190 }
    191 
    192 
    193 @@ -166,15 +184,17 @@ umtx_initImplPreInit(UInitOnce &uio) {
    194 //   Some threads may be racing to test the fState variable outside of the mutex,
    195 //   requiring the use of store/release when changing its value.
    196 
    197 U_COMMON_API void U_EXPORT2
    198 umtx_initImplPostInit(UInitOnce &uio) {
    199 +#ifndef __wasi__
    200     {
    201         std::unique_lock<std::mutex> lock(*initMutex);
    202         umtx_storeRelease(uio.fState, 2);
    203     }
    204     initCondition->notify_all();
    205 +#endif
    206 }
    207 
    208 U_NAMESPACE_END
    209 
    210 /*************************************************************************************************
    211 diff --git a/intl/icu/source/common/umutex.h b/intl/icu/source/common/umutex.h
    212 --- a/intl/icu/source/common/umutex.h
    213 +++ b/intl/icu/source/common/umutex.h
    214 @@ -18,13 +18,16 @@
    215 */
    216 
    217 #ifndef UMUTEX_H
    218 #define UMUTEX_H
    219 
    220 +#ifndef __wasi__
    221 #include <atomic>
    222 #include <condition_variable>
    223 #include <mutex>
    224 +#endif
    225 +
    226 #include <type_traits>
    227 
    228 #include "unicode/utypes.h"
    229 #include "unicode/uclean.h"
    230 #include "unicode/uobject.h"
    231 @@ -43,10 +46,12 @@ U_NAMESPACE_BEGIN
    232  *
    233  *   Low Level Atomic Operations, ICU wrappers for.
    234  *
    235  ****************************************************************************/
    236 
    237 +#ifndef __wasi__
    238 +
    239 typedef std::atomic<int32_t> u_atomic_int32_t;
    240 
    241 inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
    242     return var.load(std::memory_order_acquire);
    243 }
    244 @@ -61,10 +66,31 @@ inline int32_t umtx_atomic_inc(u_atomic_
    245 
    246 inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
    247     return var->fetch_sub(1) - 1;
    248 }
    249 
    250 +#else
    251 +
    252 +typedef int32_t u_atomic_int32_t;
    253 +
    254 +inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
    255 +    return var;
    256 +}
    257 +
    258 +inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
    259 +    var = val;
    260 +}
    261 +
    262 +inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
    263 +    return ++(*var);
    264 +}
    265 +
    266 +inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
    267 +    return --(*var);
    268 +}
    269 +
    270 +#endif
    271 
    272 /*************************************************************************************************
    273  *
    274  *  UInitOnce Definitions.
    275  *
    276 @@ -211,21 +237,29 @@ public:
    277     U_COMMON_API UMutex& operator=(const UMutex& other) = delete;
    278     U_COMMON_API void* operator new(size_t) = delete;
    279 
    280     // requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard
    281     U_COMMON_API void lock() {
    282 +#ifndef __wasi__
    283         std::mutex *m = fMutex.load(std::memory_order_acquire);
    284         if (m == nullptr) { m = getMutex(); }
    285         m->lock();
    286 +#endif
    287     }
    288 -    U_COMMON_API void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); }
    289 +    U_COMMON_API void unlock() {
    290 +#ifndef __wasi__
    291 +        fMutex.load(std::memory_order_relaxed)->unlock();
    292 +#endif
    293 +    }
    294 
    295     U_COMMON_API static void cleanup();
    296 
    297 private:
    298 +#ifndef __wasi__
    299     alignas(std::mutex) char fStorage[sizeof(std::mutex)] {};
    300     std::atomic<std::mutex *> fMutex { nullptr };
    301 +#endif
    302 
    303     /** All initialized UMutexes are kept in a linked list, so that they can be found,
    304      * and the underlying std::mutex destructed, by u_cleanup().
    305      */
    306     UMutex *fListLink { nullptr };
    307 @@ -233,11 +267,13 @@ private:
    308 
    309     /** Out-of-line function to lazily initialize a UMutex on first use.
    310      * Initial fast check is inline, in lock().  The returned value may never
    311      * be nullptr.
    312      */
    313 +#ifndef __wasi__
    314     std::mutex *getMutex();
    315 +#endif
    316 };
    317 
    318 
    319 /* Lock a mutex.
    320  * @param mutex The given mutex to be locked.  Pass NULL to specify
    321 diff --git a/intl/icu/source/common/unifiedcache.cpp b/intl/icu/source/common/unifiedcache.cpp
    322 --- a/intl/icu/source/common/unifiedcache.cpp
    323 +++ b/intl/icu/source/common/unifiedcache.cpp
    324 @@ -11,19 +11,23 @@
    325 */
    326 
    327 #include "unifiedcache.h"
    328 
    329 #include <algorithm>      // For std::max()
    330 +#ifndef __wasi__
    331 #include <mutex>
    332 +#endif
    333 
    334 #include "uassert.h"
    335 #include "uhash.h"
    336 #include "ucln_cmn.h"
    337 
    338 static icu::UnifiedCache *gCache = nullptr;
    339 +#ifndef __wasi__
    340 static std::mutex *gCacheMutex = nullptr;
    341 static std::condition_variable *gInProgressValueAddedCond;
    342 +#endif
    343 static icu::UInitOnce gCacheInitOnce {};
    344 
    345 static const int32_t MAX_EVICT_ITERATIONS = 10;
    346 static const int32_t DEFAULT_MAX_UNUSED = 1000;
    347 static const int32_t DEFAULT_PERCENTAGE_OF_IN_USE = 100;
    348 @@ -32,14 +36,16 @@ static const int32_t DEFAULT_PERCENTAGE_
    349 U_CDECL_BEGIN
    350 static UBool U_CALLCONV unifiedcache_cleanup() {
    351     gCacheInitOnce.reset();
    352     delete gCache;
    353     gCache = nullptr;
    354 +#ifndef __wasi__
    355     gCacheMutex->~mutex();
    356     gCacheMutex = nullptr;
    357     gInProgressValueAddedCond->~condition_variable();
    358     gInProgressValueAddedCond = nullptr;
    359 +#endif
    360     return true;
    361 }
    362 U_CDECL_END
    363 
    364 
    365 @@ -70,12 +76,14 @@ CacheKeyBase::~CacheKeyBase() {
    366 static void U_CALLCONV cacheInit(UErrorCode &status) {
    367     U_ASSERT(gCache == nullptr);
    368     ucln_common_registerCleanup(
    369             UCLN_COMMON_UNIFIED_CACHE, unifiedcache_cleanup);
    370 
    371 +#ifndef __wasi__
    372     gCacheMutex = STATIC_NEW(std::mutex);
    373     gInProgressValueAddedCond = STATIC_NEW(std::condition_variable);
    374 +#endif
    375     gCache = new UnifiedCache(status);
    376     if (gCache == nullptr) {
    377         status = U_MEMORY_ALLOCATION_ERROR;
    378     }
    379     if (U_FAILURE(status)) {
    380 @@ -133,41 +141,53 @@ void UnifiedCache::setEvictionPolicy(
    381     }
    382     if (count < 0 || percentageOfInUseItems < 0) {
    383         status = U_ILLEGAL_ARGUMENT_ERROR;
    384         return;
    385     }
    386 +#ifndef __wasi__
    387     std::lock_guard<std::mutex> lock(*gCacheMutex);
    388 +#endif
    389     fMaxUnused = count;
    390     fMaxPercentageOfInUse = percentageOfInUseItems;
    391 }
    392 
    393 int32_t UnifiedCache::unusedCount() const {
    394 +#ifndef __wasi__
    395     std::lock_guard<std::mutex> lock(*gCacheMutex);
    396 +#endif
    397     return uhash_count(fHashtable) - fNumValuesInUse;
    398 }
    399 
    400 int64_t UnifiedCache::autoEvictedCount() const {
    401 +#ifndef __wasi__
    402     std::lock_guard<std::mutex> lock(*gCacheMutex);
    403 +#endif
    404     return fAutoEvictedCount;
    405 }
    406 
    407 int32_t UnifiedCache::keyCount() const {
    408 +#ifndef __wasi__
    409     std::lock_guard<std::mutex> lock(*gCacheMutex);
    410 +#endif
    411     return uhash_count(fHashtable);
    412 }
    413 
    414 void UnifiedCache::flush() const {
    415 +#ifndef __wasi__
    416     std::lock_guard<std::mutex> lock(*gCacheMutex);
    417 +#endif
    418 
    419     // Use a loop in case cache items that are flushed held hard references to
    420     // other cache items making those additional cache items eligible for
    421     // flushing.
    422     while (_flush(false));
    423 }
    424 
    425 void UnifiedCache::handleUnreferencedObject() const {
    426 +#ifndef __wasi__
    427     std::lock_guard<std::mutex> lock(*gCacheMutex);
    428 +#endif
    429     --fNumValuesInUse;
    430     _runEvictionSlice();
    431 }
    432 
    433 #ifdef UNIFIED_CACHE_DEBUG
    434 @@ -182,11 +202,13 @@ void UnifiedCache::dump() {
    435     }
    436     cache->dumpContents();
    437 }
    438 
    439 void UnifiedCache::dumpContents() const {
    440 +#ifndef __wasi__
    441     std::lock_guard<std::mutex> lock(*gCacheMutex);
    442 +#endif
    443     _dumpContents();
    444 }
    445 
    446 // Dumps content of cache.
    447 // On entry, gCacheMutex must be held.
    448 @@ -222,11 +244,13 @@ UnifiedCache::~UnifiedCache() {
    449     flush();
    450     {
    451         // Now all that should be left in the cache are entries that refer to
    452         // each other and entries with hard references from outside the cache.
    453         // Nothing we can do about these so proceed to wipe out the cache.
    454 +#ifndef __wasi__
    455         std::lock_guard<std::mutex> lock(*gCacheMutex);
    456 +#endif
    457         _flush(true);
    458     }
    459     uhash_close(fHashtable);
    460     fHashtable = nullptr;
    461     delete fNoValue;
    462 @@ -323,11 +347,13 @@ void UnifiedCache::_putNew(
    463 
    464 void UnifiedCache::_putIfAbsentAndGet(
    465         const CacheKeyBase &key,
    466         const SharedObject *&value,
    467         UErrorCode &status) const {
    468 +#ifndef __wasi__
    469     std::lock_guard<std::mutex> lock(*gCacheMutex);
    470 +#endif
    471     const UHashElement *element = uhash_find(fHashtable, &key);
    472     if (element != nullptr && !_inProgress(element)) {
    473         _fetch(element, value, status);
    474         return;
    475     }
    476 @@ -348,18 +374,22 @@ UBool UnifiedCache::_poll(
    477         const CacheKeyBase &key,
    478         const SharedObject *&value,
    479         UErrorCode &status) const {
    480     U_ASSERT(value == nullptr);
    481     U_ASSERT(status == U_ZERO_ERROR);
    482 +#ifndef __wasi__
    483     std::unique_lock<std::mutex> lock(*gCacheMutex);
    484 +#endif
    485     const UHashElement *element = uhash_find(fHashtable, &key);
    486 
    487     // If the hash table contains an inProgress placeholder entry for this key,
    488     // this means that another thread is currently constructing the value object.
    489     // Loop, waiting for that construction to complete.
    490      while (element != nullptr && _inProgress(element)) {
    491 +#ifndef __wasi__
    492          gInProgressValueAddedCond->wait(lock);
    493 +#endif
    494          element = uhash_find(fHashtable, &key);
    495     }
    496 
    497     // If the hash table contains an entry for the key,
    498     // fetch out the contents and return them.
    499 @@ -426,13 +456,15 @@ void UnifiedCache::_put(
    500     UHashElement *ptr = const_cast<UHashElement *>(element);
    501     ptr->value.pointer = (void *) value;
    502     U_ASSERT(oldValue == fNoValue);
    503     removeSoftRef(oldValue);
    504 
    505 +#ifndef __wasi__
    506     // Tell waiting threads that we replace in-progress status with
    507     // an error.
    508     gInProgressValueAddedCond->notify_all();
    509 +#endif
    510 }
    511 
    512 void UnifiedCache::_fetch(
    513         const UHashElement *element,
    514         const SharedObject *&value,
    515 diff --git a/intl/icu/source/i18n/decContext.h b/intl/icu/source/i18n/decContext.h
    516 --- a/intl/icu/source/i18n/decContext.h
    517 +++ b/intl/icu/source/i18n/decContext.h
    518 @@ -59,11 +59,13 @@
    519 
    520   #if !defined(int32_t)
    521 /* #include <stdint.h>   */         /* C99 standard integers           */
    522   #endif
    523   #include <stdio.h>               /* for printf, etc.                */
    524 +#ifndef __wasi__
    525   #include <signal.h>              /* for traps                       */
    526 +#endif
    527 
    528   /* Extended flags setting -- set this to 0 to use only IEEE flags   */
    529   #if !defined(DECEXTFLAG)
    530   #define DECEXTFLAG 1             /* 1=enable extended flags         */
    531   #endif
    532 diff --git a/intl/icu/source/i18n/decimfmt.cpp b/intl/icu/source/i18n/decimfmt.cpp
    533 --- a/intl/icu/source/i18n/decimfmt.cpp
    534 +++ b/intl/icu/source/i18n/decimfmt.cpp
    535 @@ -478,12 +478,17 @@ DecimalFormat& DecimalFormat::operator=(
    536 }
    537 
    538 DecimalFormat::~DecimalFormat() {
    539     if (fields == nullptr) { return; }
    540 
    541 +#ifndef __wasi__
    542     delete fields->atomicParser.exchange(nullptr);
    543     delete fields->atomicCurrencyParser.exchange(nullptr);
    544 +#else
    545 +    delete fields->atomicParser;
    546 +    delete fields->atomicCurrencyParser;
    547 +#endif
    548     delete fields;
    549 }
    550 
    551 DecimalFormat* DecimalFormat::clone() const {
    552     // can only clone valid objects.
    553 @@ -1635,12 +1640,17 @@ void DecimalFormat::touch(UErrorCode& st
    554     
    555     // Do this after fields->exportedProperties are set up
    556     setupFastFormat();
    557 
    558     // Delete the parsers if they were made previously
    559 +#ifndef __wasi__
    560     delete fields->atomicParser.exchange(nullptr);
    561     delete fields->atomicCurrencyParser.exchange(nullptr);
    562 +#else
    563 +    delete fields->atomicParser;
    564 +    delete fields->atomicCurrencyParser;
    565 +#endif
    566 
    567     // In order for the getters to work, we need to populate some fields in NumberFormat.
    568     NumberFormat::setCurrency(fields->exportedProperties.currency.get(status).getISOCurrency(), status);
    569     NumberFormat::setMaximumIntegerDigits(fields->exportedProperties.maximumIntegerDigits);
    570     NumberFormat::setMinimumIntegerDigits(fields->exportedProperties.minimumIntegerDigits);
    571 @@ -1671,11 +1681,15 @@ const numparse::impl::NumberParserImpl* 
    572     if (U_FAILURE(status)) {
    573         return nullptr;
    574     }
    575 
    576     // First try to get the pre-computed parser
    577 +#ifndef __wasi__
    578     auto* ptr = fields->atomicParser.load();
    579 +#else
    580 +    auto* ptr = fields->atomicParser;
    581 +#endif
    582     if (ptr != nullptr) {
    583         return ptr;
    584     }
    585 
    586     // Try computing the parser on our own
    587 @@ -1690,25 +1704,34 @@ const numparse::impl::NumberParserImpl* 
    588 
    589     // Note: ptr starts as nullptr; during compare_exchange,
    590     // it is set to what is actually stored in the atomic
    591     // if another thread beat us to computing the parser object.
    592     auto* nonConstThis = const_cast<DecimalFormat*>(this);
    593 +#ifndef __wasi__
    594     if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) {
    595         // Another thread beat us to computing the parser
    596         delete temp;
    597         return ptr;
    598     } else {
    599         // Our copy of the parser got stored in the atomic
    600         return temp;
    601     }
    602 +#else
    603 +    nonConstThis->fields->atomicParser = temp;
    604 +    return temp;
    605 +#endif
    606 }
    607 
    608 const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const {
    609     if (U_FAILURE(status)) { return nullptr; }
    610 
    611     // First try to get the pre-computed parser
    612 +#ifndef __wasi__
    613     auto* ptr = fields->atomicCurrencyParser.load();
    614 +#else
    615 +    auto* ptr = fields->atomicCurrencyParser;
    616 +#endif
    617     if (ptr != nullptr) {
    618         return ptr;
    619     }
    620 
    621     // Try computing the parser on our own
    622 @@ -1719,18 +1742,23 @@ const numparse::impl::NumberParserImpl* 
    623     }
    624 
    625     // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the
    626     // atomic if another thread beat us to computing the parser object.
    627     auto* nonConstThis = const_cast<DecimalFormat*>(this);
    628 +#ifndef __wasi__
    629     if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) {
    630         // Another thread beat us to computing the parser
    631         delete temp;
    632         return ptr;
    633     } else {
    634         // Our copy of the parser got stored in the atomic
    635         return temp;
    636     }
    637 +#else
    638 +    nonConstThis->fields->atomicCurrencyParser = temp;
    639 +    return temp;
    640 +#endif
    641 }
    642 
    643 void
    644 DecimalFormat::fieldPositionHelper(
    645         const UFormattedNumberData& formatted,
    646 diff --git a/intl/icu/source/i18n/number_mapper.h b/intl/icu/source/i18n/number_mapper.h
    647 --- a/intl/icu/source/i18n/number_mapper.h
    648 +++ b/intl/icu/source/i18n/number_mapper.h
    649 @@ -5,18 +5,21 @@
    650 
    651 #if !UCONFIG_NO_FORMATTING
    652 #ifndef __NUMBER_MAPPER_H__
    653 #define __NUMBER_MAPPER_H__
    654 
    655 -#include <atomic>
    656 #include "number_types.h"
    657 #include "unicode/currpinf.h"
    658 #include "standardplural.h"
    659 #include "number_patternstring.h"
    660 #include "number_currencysymbols.h"
    661 #include "numparse_impl.h"
    662 
    663 +#ifndef __wasi__
    664 +#include <atomic>
    665 +#endif
    666 +
    667 U_NAMESPACE_BEGIN
    668 namespace number::impl {
    669 
    670 class AutoAffixPatternProvider;
    671 class CurrencyPluralInfoAffixProvider;
    672 @@ -194,14 +197,22 @@ struct DecimalFormatFields : public UMem
    673     * #format} method uses the formatter directly without needing to synchronize.
    674     */
    675     LocalizedNumberFormatter formatter;
    676 
    677     /** The lazy-computed parser for .parse() */
    678 +#ifndef __wasi__
    679     std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicParser = {};
    680 +#else
    681 +    ::icu::numparse::impl::NumberParserImpl* atomicParser = nullptr;
    682 +#endif
    683 
    684     /** The lazy-computed parser for .parseCurrency() */
    685 +#ifndef __wasi__
    686     std::atomic<::icu::numparse::impl::NumberParserImpl*> atomicCurrencyParser = {};
    687 +#else
    688 +    ::icu::numparse::impl::NumberParserImpl* atomicCurrencyParser = {};
    689 +#endif
    690 
    691     /** Small object ownership warehouse for the formatter and parser */
    692     DecimalFormatWarehouse warehouse;
    693 
    694     /** The effective properties as exported from the formatter object. Used by some getters. */
    695 diff --git a/intl/icu/source/i18n/numrange_fluent.cpp b/intl/icu/source/i18n/numrange_fluent.cpp
    696 --- a/intl/icu/source/i18n/numrange_fluent.cpp
    697 +++ b/intl/icu/source/i18n/numrange_fluent.cpp
    698 @@ -246,33 +246,53 @@ LocalizedNumberRangeFormatter::Localized
    699 
    700 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) noexcept
    701         : NFS<LNF>(std::move(src)) {
    702     // Steal the compiled formatter
    703     LNF&& _src = static_cast<LNF&&>(src);
    704 +#ifndef __wasi__
    705     auto* stolen = _src.fAtomicFormatter.exchange(nullptr);
    706     delete fAtomicFormatter.exchange(stolen);
    707 +#else
    708 +    delete fAtomicFormatter;
    709 +    fAtomicFormatter = _src.fAtomicFormatter;
    710 +    _src.fAtomicFormatter = nullptr;
    711 +#endif
    712 }
    713 
    714 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) {
    715     if (this == &other) { return *this; }  // self-assignment: no-op
    716     NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
    717     // Do not steal; just clear
    718 +#ifndef __wasi__
    719     delete fAtomicFormatter.exchange(nullptr);
    720 +#else
    721 +    delete fAtomicFormatter;
    722 +#endif
    723     return *this;
    724 }
    725 
    726 LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) noexcept {
    727     NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
    728     // Steal the compiled formatter
    729 +#ifndef __wasi__
    730     auto* stolen = src.fAtomicFormatter.exchange(nullptr);
    731     delete fAtomicFormatter.exchange(stolen);
    732 +#else
    733 +    delete fAtomicFormatter;
    734 +    fAtomicFormatter = src.fAtomicFormatter;
    735 +    src.fAtomicFormatter = nullptr;
    736 +#endif
    737     return *this;
    738 }
    739 
    740 
    741 LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() {
    742 +#ifndef __wasi__
    743     delete fAtomicFormatter.exchange(nullptr);
    744 +#else
    745 +    delete fAtomicFormatter;
    746 +#endif
    747 }
    748 
    749 LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
    750     fMacros = macros;
    751     fMacros.locale = locale;
    752 @@ -363,11 +383,15 @@ LocalizedNumberRangeFormatter::getFormat
    753     if (U_FAILURE(status)) {
    754         return nullptr;
    755     }
    756 
    757     // First try to get the pre-computed formatter
    758 +#ifndef __wasi__
    759     auto* ptr = fAtomicFormatter.load();
    760 +#else
    761 +    auto* ptr = fAtomicFormatter;
    762 +#endif
    763     if (ptr != nullptr) {
    764         return ptr;
    765     }
    766 
    767     // Try computing the formatter on our own
    768 @@ -378,17 +402,22 @@ LocalizedNumberRangeFormatter::getFormat
    769 
    770     // Note: ptr starts as nullptr; during compare_exchange,
    771     // it is set to what is actually stored in the atomic
    772     // if another thread beat us to computing the formatter object.
    773     auto* nonConstThis = const_cast<LocalizedNumberRangeFormatter*>(this);
    774 +#ifndef __wasi__
    775     if (!nonConstThis->fAtomicFormatter.compare_exchange_strong(ptr, temp.getAlias())) {
    776         // Another thread beat us to computing the formatter
    777         return ptr;
    778     } else {
    779         // Our copy of the formatter got stored in the atomic
    780         return temp.orphan();
    781     }
    782 +#else
    783 +    nonConstThis->fAtomicFormatter = temp.getAlias();
    784 +    return temp.orphan();
    785 +#endif
    786 
    787 }
    788 
    789 
    790 #endif /* #if !UCONFIG_NO_FORMATTING */
    791 diff --git a/intl/icu/source/i18n/unicode/numberrangeformatter.h b/intl/icu/source/i18n/unicode/numberrangeformatter.h
    792 --- a/intl/icu/source/i18n/unicode/numberrangeformatter.h
    793 +++ b/intl/icu/source/i18n/unicode/numberrangeformatter.h
    794 @@ -8,18 +8,21 @@
    795 
    796 #if U_SHOW_CPLUSPLUS_API
    797 
    798 #if !UCONFIG_NO_FORMATTING
    799 
    800 -#include <atomic>
    801 #include "unicode/appendable.h"
    802 #include "unicode/fieldpos.h"
    803 #include "unicode/formattedvalue.h"
    804 #include "unicode/fpositer.h"
    805 #include "unicode/numberformatter.h"
    806 #include "unicode/unumberrangeformatter.h"
    807 
    808 +#ifndef __wasi__
    809 +#include <atomic>
    810 +#endif
    811 +
    812 /**
    813  * \file
    814  * \brief C++ API: Library for localized formatting of number, currency, and unit ranges.
    815  *
    816  * The main entrypoint to the formatting of ranges of numbers, including currencies and other units of measurement.
    817 @@ -559,11 +562,15 @@ class U_I18N_API_CLASS LocalizedNumberRa
    818      * @stable ICU 63
    819      */
    820     U_I18N_API ~LocalizedNumberRangeFormatter();
    821 
    822   private:
    823 +#ifndef __wasi__
    824     std::atomic<impl::NumberRangeFormatterImpl*> fAtomicFormatter = {};
    825 +#else
    826 +    impl::NumberRangeFormatterImpl* fAtomicFormatter = nullptr;
    827 +#endif
    828 
    829     const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) const;
    830 
    831     explicit LocalizedNumberRangeFormatter(
    832         const NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>& other);