Skip to content

Add cross-platform atomics #327

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

Merged
merged 3 commits into from
Apr 2, 2024
Merged
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
12 changes: 9 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ if(MSVC)
xcheck_add_c_compiler_flag(-Wno-reserved-macro-identifier)
xcheck_add_c_compiler_flag(-Wno-reserved-identifier)
xcheck_add_c_compiler_flag(-Wdeprecated-declarations)
add_compile_definitions(WIN32_LEAN_AND_MEAN)
xcheck_add_c_compiler_flag(/experimental:c11atomics)
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "WASI")
Expand Down Expand Up @@ -153,10 +153,16 @@ if(BUILD_QJS_LIBC)
list(APPEND qjs_sources quickjs-libc.c)
endif()
list(APPEND qjs_defines _GNU_SOURCE)
if(WIN32)
list(APPEND qjs_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602)
endif()
list(APPEND qjs_libs qjs ${CMAKE_DL_LIBS})
find_package(Threads)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "WASI")
list(APPEND qjs_libs ${CMAKE_THREAD_LIBS_INIT})
endif()
if(NOT MSVC)
find_package(Threads)
list(APPEND qjs_libs ${CMAKE_THREAD_LIBS_INIT} m)
list(APPEND qjs_libs m)
endif()

add_library(qjs ${qjs_sources})
Expand Down
222 changes: 220 additions & 2 deletions cutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@

#include "cutils.h"

#undef NANOSEC
#define NANOSEC ((uint64_t) 1e9)

#pragma GCC visibility push(default)

void pstrcpy(char *buf, int buf_size, const char *str)
Expand Down Expand Up @@ -649,7 +652,7 @@ uint64_t js__hrtime_ns(void) {
* performance counter interval, integer math could cause this computation
* to overflow. Therefore we resort to floating point math.
*/
scaled_freq = (double) frequency.QuadPart / 1e9;
scaled_freq = (double) frequency.QuadPart / NANOSEC;
result = (double) counter.QuadPart / scaled_freq;
return (uint64_t) result;
}
Expand All @@ -660,7 +663,7 @@ uint64_t js__hrtime_ns(void) {
if (clock_gettime(CLOCK_MONOTONIC, &t))
abort();

return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
return t.tv_sec * NANOSEC + t.tv_nsec;
}
#endif

Expand All @@ -674,4 +677,219 @@ int64_t js__gettimeofday_us(void) {
return ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
}

/* Cross-platform threading APIs. */

#if !defined(EMSCRIPTEN) && !defined(__wasi__)

#if defined(_WIN32)
typedef void (*js__once_cb)(void);

typedef struct {
js__once_cb callback;
} js__once_data_t;

static BOOL WINAPI js__once_inner(INIT_ONCE *once, void *param, void **context) {
js__once_data_t *data = param;

data->callback();

return TRUE;
}

void js_once(js_once_t *guard, js__once_cb callback) {
js__once_data_t data = { .callback = callback };
InitOnceExecuteOnce(guard, js__once_inner, (void*) &data, NULL);
}

void js_mutex_init(js_mutex_t *mutex) {
InitializeCriticalSection(mutex);
}

void js_mutex_destroy(js_mutex_t *mutex) {
DeleteCriticalSection(mutex);
}

void js_mutex_lock(js_mutex_t *mutex) {
EnterCriticalSection(mutex);
}

void js_mutex_unlock(js_mutex_t *mutex) {
LeaveCriticalSection(mutex);
}

void js_cond_init(js_cond_t *cond) {
InitializeConditionVariable(cond);
}

void js_cond_destroy(js_cond_t *cond) {
/* nothing to do */
(void) cond;
}

void js_cond_signal(js_cond_t *cond) {
WakeConditionVariable(cond);
}

void js_cond_broadcast(js_cond_t *cond) {
WakeAllConditionVariable(cond);
}

void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) {
if (!SleepConditionVariableCS(cond, mutex, INFINITE))
abort();
}

int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) {
if (SleepConditionVariableCS(cond, mutex, (DWORD)(timeout / 1e6)))
return 0;
if (GetLastError() != ERROR_TIMEOUT)
abort();
return -1;
}

#else /* !defined(_WIN32) */

void js_once(js_once_t *guard, void (*callback)(void)) {
if (pthread_once(guard, callback))
abort();
}

void js_mutex_init(js_mutex_t *mutex) {
if (pthread_mutex_init(mutex, NULL))
abort();
}

void js_mutex_destroy(js_mutex_t *mutex) {
if (pthread_mutex_destroy(mutex))
abort();
}

void js_mutex_lock(js_mutex_t *mutex) {
if (pthread_mutex_lock(mutex))
abort();
}

void js_mutex_unlock(js_mutex_t *mutex) {
if (pthread_mutex_unlock(mutex))
abort();
}

void js_cond_init(js_cond_t *cond) {
#if defined(__APPLE__) && defined(__MACH__)
if (pthread_cond_init(cond, NULL))
abort();
#else
pthread_condattr_t attr;
int err;

if (pthread_condattr_init(&attr))
abort();

if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC))
abort();

if (pthread_cond_init(cond, &attr))
abort();

if (pthread_condattr_destroy(&attr))
abort();
#endif
}

void js_cond_destroy(js_cond_t *cond) {
#if defined(__APPLE__) && defined(__MACH__)
/* It has been reported that destroying condition variables that have been
* signalled but not waited on can sometimes result in application crashes.
* See https://codereview.chromium.org/1323293005.
*/
pthread_mutex_t mutex;
struct timespec ts;
int err;

if (pthread_mutex_init(&mutex, NULL))
abort();

if (pthread_mutex_lock(&mutex))
abort();

ts.tv_sec = 0;
ts.tv_nsec = 1;

err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts);
if (err != 0 && err != ETIMEDOUT)
abort();

if (pthread_mutex_unlock(&mutex))
abort();

if (pthread_mutex_destroy(&mutex))
abort();
#endif /* defined(__APPLE__) && defined(__MACH__) */

if (pthread_cond_destroy(cond))
abort();
}

void js_cond_signal(js_cond_t *cond) {
if (pthread_cond_signal(cond))
abort();
}

void js_cond_broadcast(js_cond_t *cond) {
if (pthread_cond_broadcast(cond))
abort();
}

void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) {
#if defined(__APPLE__) && defined(__MACH__)
int r;

errno = 0;
r = pthread_cond_wait(cond, mutex);

/* Workaround for a bug in OS X at least up to 13.6
* See https://github.com/libuv/libuv/issues/4165
*/
if (r == EINVAL && errno == EBUSY)
return;
if (r)
abort();
#else
if (pthread_cond_wait(cond, mutex))
abort();
#endif
}

int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) {
int r;
struct timespec ts;

#if !defined(__APPLE__)
timeout += js__hrtime_ns();
#endif

ts.tv_sec = timeout / NANOSEC;
ts.tv_nsec = timeout % NANOSEC;
#if defined(__APPLE__) && defined(__MACH__)
r = pthread_cond_timedwait_relative_np(cond, mutex, &ts);
#else
r = pthread_cond_timedwait(cond, mutex, &ts);
#endif

if (r == 0)
return 0;

if (r == ETIMEDOUT)
return -1;

abort();

/* Pacify some compilers. */
return -1;
}

#endif

#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */

#pragma GCC visibility pop
41 changes: 39 additions & 2 deletions cutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
#include <string.h>
#include <inttypes.h>

#if defined(_MSC_VER)
#if defined(_WIN32)
#include <windows.h>
#endif
#if defined(_MSC_VER)
#include <winsock2.h>
#include <malloc.h>
#define alloca _alloca
Expand All @@ -43,7 +45,10 @@
#elif defined(__FreeBSD__)
#include <malloc_np.h>
#endif

#if !defined(_WIN32)
#include <errno.h>
#include <pthread.h>
#endif

#if defined(_MSC_VER) && !defined(__clang__)
# define likely(x) (x)
Expand Down Expand Up @@ -426,4 +431,36 @@ static inline size_t js__malloc_usable_size(const void *ptr)
#endif
}

/* Cross-platform threading APIs. */

#if !defined(EMSCRIPTEN) && !defined(__wasi__)

#if defined(_WIN32)
#define JS_ONCE_INIT INIT_ONCE_STATIC_INIT
typedef INIT_ONCE js_once_t;
typedef CRITICAL_SECTION js_mutex_t;
typedef CONDITION_VARIABLE js_cond_t;
#else
#define JS_ONCE_INIT PTHREAD_ONCE_INIT
typedef pthread_once_t js_once_t;
typedef pthread_mutex_t js_mutex_t;
typedef pthread_cond_t js_cond_t;
#endif

void js_once(js_once_t *guard, void (*callback)(void));

void js_mutex_init(js_mutex_t *mutex);
void js_mutex_destroy(js_mutex_t *mutex);
void js_mutex_lock(js_mutex_t *mutex);
void js_mutex_unlock(js_mutex_t *mutex);

void js_cond_init(js_cond_t *cond);
void js_cond_destroy(js_cond_t *cond);
void js_cond_signal(js_cond_t *cond);
void js_cond_broadcast(js_cond_t *cond);
void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex);
int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout);

#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */

#endif /* CUTILS_H */
Loading