Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICU-22838 Add WebAssembly/WASI cross-compilation support #3067

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions icu4c/source/acinclude.m4
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ powerpc*-apple-darwin*) icu_cv_host_frag=mh-darwin-ppc ;;
*-dec-osf*) icu_cv_host_frag=mh-alpha-osf ;;
*-*-nto*) icu_cv_host_frag=mh-qnx ;;
*-ncr-*) icu_cv_host_frag=mh-mpras ;;
*-wasi*) icu_cv_host_frag=mh-wasi ;;
*) icu_cv_host_frag=mh-unknown ;;
esac
]
Expand Down
6 changes: 6 additions & 0 deletions icu4c/source/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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: please build this in GitHub Actions, because otherwise it could break again at any time.

#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
18 changes: 18 additions & 0 deletions icu4c/source/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,7 +56,9 @@ std::once_flag initFlag;
std::once_flag *pInitFlag = &initFlag;

} // Anonymous namespace
#endif

#if U_HAVE_ATOMICS
U_CDECL_BEGIN
static UBool U_CALLCONV umtx_cleanup() {
initMutex->~mutex();
Expand All @@ -75,8 +78,10 @@ static void U_CALLCONV umtx_init() {
ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup);
}
U_CDECL_END
#endif


#if U_HAVE_ATOMICS
std::mutex *UMutex::getMutex() {
std::mutex *retPtr = fMutex.load(std::memory_order_acquire);
if (retPtr == nullptr) {
Expand All @@ -93,14 +98,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 +118,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 +155,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 +184,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 icu4c/source/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 "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_HAVE_ATOMICS

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 // U_HAVE_ATOMICS

/*************************************************************************************************
*
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
14 changes: 14 additions & 0 deletions icu4c/source/common/unicode/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,20 @@
# define U_PLATFORM_IS_DARWIN_BASED 0
#endif

/**
* \def U_HAVE_ATOMICS
* Defines whether the platform supports atomic operations.
* @internal
Comment on lines +317 to +319
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: maybe name it U_ATOMICS_UNSAFE_POLYFILL or something like that to indicate that this is not disabling atomic code but is instead replacing it with a non-atomic alternative.

*/
#ifdef U_HAVE_ATOMICS
/* Use the predefined value. */
#elif defined(__wasi__) && !defined(_REENTRANT)
/* WASI does not support atomics when wasi-threads feature is not enabled */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why not use wasi-threads, then? It seems error-prone to add a flag that skips mutexes.

# define U_HAVE_ATOMICS 0
#else
# define U_HAVE_ATOMICS 1
#endif

/*===========================================================================*/
/** @{ Compiler and environment features */
/*===========================================================================*/
Expand Down
Loading