Skip to content

Commit

Permalink
Cherry-pick "ICU-22838 Add WebAssembly/WASI cross-compilation support"
Browse files Browse the repository at this point in the history
The original patch is still under review in the upstream ICU project, but
it is needed to unblock the swift-foundation build on WebAssembly.

See unicode-org/icu#3067
  • Loading branch information
kateinoigakukun committed Aug 2, 2024
1 parent b7cb010 commit a36358f
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 20 deletions.
6 changes: 6 additions & 0 deletions icuSources/common/putilimp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
20 changes: 20 additions & 0 deletions icuSources/common/umutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ U_NAMESPACE_BEGIN
*
*************************************************************************************************/

#if U_HAVE_ATOMICS
namespace {
std::mutex *initMutex;
std::condition_variable *initCondition;
Expand All @@ -55,9 +56,11 @@ std::once_flag initFlag;
std::once_flag *pInitFlag = &initFlag;

} // Anonymous namespace
#endif

U_CDECL_BEGIN
static UBool U_CALLCONV umtx_cleanup() {
#if U_HAVE_ATOMICS
initMutex->~mutex();
initCondition->~condition_variable();
UMutex::cleanup();
Expand All @@ -66,17 +69,21 @@ static UBool U_CALLCONV umtx_cleanup() {
// Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once().
pInitFlag->~once_flag();
pInitFlag = new(&initFlag) std::once_flag();
#endif
return true;
}

static void U_CALLCONV umtx_init() {
#if U_HAVE_ATOMICS
initMutex = STATIC_NEW(std::mutex);
initCondition = STATIC_NEW(std::condition_variable);
ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
#endif
}
U_CDECL_END


#if U_HAVE_ATOMICS
std::mutex *UMutex::getMutex() {
std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
if (retPtr == nullptr) {
Expand All @@ -93,14 +100,17 @@ std::mutex *UMutex::getMutex() {
U_ASSERT(retPtr != nullptr);
return retPtr;
}
#endif

UMutex *UMutex::gListHead = nullptr;

void UMutex::cleanup() {
UMutex *next = nullptr;
for (UMutex *m = gListHead; m != nullptr; m = next) {
#if U_HAVE_ATOMICS
(*m->fMutex).~mutex();
m->fMutex = nullptr;
#endif
next = m->fListLink;
m->fListLink = nullptr;
}
Expand All @@ -110,20 +120,24 @@ void UMutex::cleanup() {

U_CAPI void U_EXPORT2
umtx_lock(UMutex *mutex) {
#if U_HAVE_ATOMICS
if (mutex == nullptr) {
mutex = &globalMutex;
}
mutex->lock();
#endif
}


U_CAPI void U_EXPORT2
umtx_unlock(UMutex* mutex)
{
#if U_HAVE_ATOMICS
if (mutex == nullptr) {
mutex = &globalMutex;
}
mutex->unlock();
#endif
}


Expand All @@ -143,18 +157,22 @@ umtx_unlock(UMutex* mutex)
//
U_COMMON_API UBool U_EXPORT2
umtx_initImplPreInit(UInitOnce &uio) {
#if U_HAVE_ATOMICS
std::call_once(*pInitFlag, umtx_init);
std::unique_lock<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 {
#if U_HAVE_ATOMICS
while (umtx_loadAcquire(uio.fState) == 1) {
// Another thread is currently running the initialization.
// Wait until it completes.
initCondition->wait(lock);
}
U_ASSERT(uio.fState == 2);
#endif
return false;
}
}
Expand All @@ -168,11 +186,13 @@ umtx_initImplPreInit(UInitOnce &uio) {

U_COMMON_API void U_EXPORT2
umtx_initImplPostInit(UInitOnce &uio) {
#if U_HAVE_ATOMICS
{
std::unique_lock<std::mutex> lock(*initMutex);
umtx_storeRelease(uio.fState, 2);
}
initCondition->notify_all();
#endif
}

U_NAMESPACE_END
Expand Down
47 changes: 43 additions & 4 deletions icuSources/common/umutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
#ifndef UMUTEX_H
#define UMUTEX_H

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <type_traits>

#include <_foundation_unicode/utypes.h>
Expand All @@ -37,6 +34,12 @@
#error U_USER_ATOMICS and U_USER_MUTEX_H are not supported
#endif

#if U_HAVE_ATOMICS

#include <atomic>
#include <condition_variable>
#include <mutex>

// 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.
Expand All @@ -61,6 +64,7 @@ template struct std::atomic<std::mutex *>;
#endif
#endif

#endif

U_NAMESPACE_BEGIN

Expand All @@ -70,6 +74,8 @@ U_NAMESPACE_BEGIN
*
****************************************************************************/

#if U_HAVE_ATOMICS

typedef std::atomic<int32_t> u_atomic_int32_t;

inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
Expand All @@ -88,6 +94,29 @@ inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
return var->fetch_sub(1) - 1;
}

#else

// No atomic operations available. Use a simple int32_t instead.

typedef int32_t u_atomic_int32_t;

inline int32_t umtx_loadAcquire(u_atomic_int32_t &var) {
return var;
}

inline void umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
var = val;
}

inline int32_t umtx_atomic_inc(u_atomic_int32_t *var) {
return ++(*var);
}

inline int32_t umtx_atomic_dec(u_atomic_int32_t *var) {
return --(*var);
}

#endif

/*************************************************************************************************
*
Expand Down Expand Up @@ -227,17 +256,25 @@ class U_COMMON_API UMutex {

// requirements for C++ BasicLockable, allows UMutex to work with std::lock_guard
void lock() {
#if U_HAVE_ATOMICS
std::mutex *m = fMutex.load(std::memory_order_acquire);
if (m == nullptr) { m = getMutex(); }
m->lock();
#endif
}
void unlock() {
#if U_HAVE_ATOMICS
fMutex.load(std::memory_order_relaxed)->unlock();
#endif
}
void unlock() { fMutex.load(std::memory_order_relaxed)->unlock(); }

static void cleanup();

private:
#if U_HAVE_ATOMICS
alignas(std::mutex) char fStorage[sizeof(std::mutex)] {};
std::atomic<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().
Expand All @@ -249,7 +286,9 @@ class U_COMMON_API UMutex {
* Initial fast check is inline, in lock(). The returned value may never
* be nullptr.
*/
#if U_HAVE_ATOMICS
std::mutex *getMutex();
#endif
};


Expand Down
Loading

0 comments on commit a36358f

Please sign in to comment.