Skip to content

Commit

Permalink
Apply WASI support patch against ICU source tree (#35) (#43)
Browse files Browse the repository at this point in the history
* Cherry-pick "ICU-22838 Add WebAssembly/WASI cross-compilation support"

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

* [Build] Update compile definitions for WASI target

`U_TIMEZONE` must not be defined and dynamic loading features must be
disabled for WASI target.

Co-authored-by: Yuta Saito <[email protected]>
  • Loading branch information
iCharlesHu and kateinoigakukun authored Aug 7, 2024
1 parent 8a12a1c commit be50023
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 26 deletions.
14 changes: 8 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,22 @@ if(CMAKE_SYSTEM_NAME STREQUAL Windows)
$<$<COMPILE_LANGUAGE:C,CXX>:U_TIMEZONE=_timezone>
$<$<COMPILE_LANGUAGE:C,CXX>:_CRT_SECURE_NO_DEPRECATE>
$<$<COMPILE_LANGUAGE:C,CXX>:U_PLATFORM_USES_ONLY_WIN32_API>)
else()
add_compile_definitions(
$<$<COMPILE_LANGUAGE:C,CXX>:U_TIMEZONE=timezone>)
endif()
# WASI specific settings
if(CMAKE_SYSTEM_NAME STREQUAL WASI)
elseif(CMAKE_SYSTEM_NAME STREQUAL WASI)
# WASI specific settings
add_compile_definitions(
$<$<COMPILE_LANGUAGE:C,CXX>:U_HAVE_TZSET=0>
$<$<COMPILE_LANGUAGE:C,CXX>:U_HAVE_TZNAME=0>
$<$<COMPILE_LANGUAGE:C,CXX>:U_HAVE_TIMEZONE=0>
$<$<COMPILE_LANGUAGE:C,CXX>:HAVE_DLFCN_H=0>
$<$<COMPILE_LANGUAGE:C,CXX>:HAVE_DLOPEN=0>
$<$<COMPILE_LANGUAGE:C,CXX>:U_ENABLE_DYLOAD=0>
$<$<COMPILE_LANGUAGE:C,CXX>:_WASI_EMULATED_SIGNAL>
$<$<COMPILE_LANGUAGE:C,CXX>:_WASI_EMULATED_MMAN>)
add_link_options("-Lwasi-emulated-signal")
add_link_options("-Lwasi-emulated-mman")
else()
add_compile_definitions(
$<$<COMPILE_LANGUAGE:C,CXX>:U_TIMEZONE=timezone>)
endif()

if(BUILD_SHARED_LIBS)
Expand Down
4 changes: 4 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ var buildSettings: [CXXSetting] = [
.define("U_COMMON_IMPLEMENTATION"),
.define("U_I18N_IMPLEMENTATION"),
.define("U_IO_IMPLEMENTATION"),
.define("HAVE_DLFCN_H", to: "0", .when(platforms: [.wasi])),
.define("HAVE_DLOPEN", to: "0", .when(platforms: [.wasi])),
.define("U_ENABLE_DYLOAD", to: "0", .when(platforms: [.wasi])),

// Where data are stored
.define("ICU_DATA_DIR", to: "\"/usr/share/icu/\""),
.define("USE_PACKAGE_DATA", to: "1"),
Expand Down
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 be50023

Please sign in to comment.