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);