mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Implement uv_mutex_* fns and others (#18555)
This commit is contained in:
@@ -142,6 +142,14 @@ if(UNIX)
|
||||
-fno-unwind-tables
|
||||
-fno-asynchronous-unwind-tables
|
||||
)
|
||||
|
||||
# needed for libuv stubs because they use
|
||||
# C23 feature which lets you define parameter without
|
||||
# name
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Allow C23 extensions"
|
||||
-Wno-c23-extensions
|
||||
)
|
||||
endif()
|
||||
|
||||
register_compiler_flags(
|
||||
|
||||
@@ -635,6 +635,8 @@ file(GLOB BUN_C_SOURCES ${CONFIGURE_DEPENDS}
|
||||
${BUN_USOCKETS_SOURCE}/src/eventing/*.c
|
||||
${BUN_USOCKETS_SOURCE}/src/internal/*.c
|
||||
${BUN_USOCKETS_SOURCE}/src/crypto/*.c
|
||||
${CWD}/src/bun.js/bindings/uv-posix-polyfills.c
|
||||
${CWD}/src/bun.js/bindings/uv-posix-stubs.c
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -274,9 +274,9 @@ ${parts.map(([stub, _]) => stub).join("\n\n")}
|
||||
|
||||
`;
|
||||
|
||||
await Bun.write(join(import.meta.dir, "../", "uv-posix-stubs.cpp"), final_contents);
|
||||
await Bun.write(join(import.meta.dir, "../", "uv-posix-stubs.c"), final_contents);
|
||||
if (Bun.which("clang-format")) {
|
||||
await Bun.$`clang-format -i ${join(import.meta.dir, "../", "uv-posix-stubs.cpp")}`;
|
||||
await Bun.$`clang-format -i ${join(import.meta.dir, "../", "uv-posix-stubs.c")}`;
|
||||
}
|
||||
|
||||
const test_plugin_contents = ` // GENERATED CODE ... NO TOUCHY!!
|
||||
|
||||
@@ -123,7 +123,8 @@ export const symbols = [
|
||||
"uv_handle_size",
|
||||
"uv_handle_type_name",
|
||||
"uv_has_ref",
|
||||
"uv_hrtime",
|
||||
// Defined in uv-posix-polyfills.cpp
|
||||
// "uv_hrtime",
|
||||
"uv_idle_init",
|
||||
"uv_idle_start",
|
||||
"uv_idle_stop",
|
||||
@@ -161,14 +162,16 @@ export const symbols = [
|
||||
"uv_loop_size",
|
||||
"uv_metrics_idle_time",
|
||||
"uv_metrics_info",
|
||||
"uv_mutex_destroy",
|
||||
"uv_mutex_init",
|
||||
"uv_mutex_init_recursive",
|
||||
"uv_mutex_lock",
|
||||
"uv_mutex_trylock",
|
||||
"uv_mutex_unlock",
|
||||
// Defined in uv-posix-polyfills.cpp
|
||||
// "uv_mutex_destroy",
|
||||
// "uv_mutex_init",
|
||||
// "uv_mutex_init_recursive",
|
||||
// "uv_mutex_lock",
|
||||
// "uv_mutex_trylock",
|
||||
// "uv_mutex_unlock",
|
||||
"uv_now",
|
||||
"uv_once",
|
||||
// Defined in uv-posix-polyfills.cpp
|
||||
// "uv_once",
|
||||
"uv_open_osfhandle",
|
||||
"uv_os_environ",
|
||||
"uv_os_free_environ",
|
||||
|
||||
@@ -71,6 +71,9 @@
|
||||
#define WEBCORE_EXPORT JS_EXPORT_PRIVATE
|
||||
#endif
|
||||
|
||||
#include <wtf/Platform.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <wtf/PlatformCallingConventions.h>
|
||||
#include <JavaScriptCore/JSCJSValue.h>
|
||||
#include <wtf/text/MakeString.h>
|
||||
@@ -78,6 +81,7 @@
|
||||
#include <JavaScriptCore/HandleSet.h>
|
||||
#include <wtf/Ref.h>
|
||||
#include <wtf/ThreadSafeRefCounted.h>
|
||||
#endif
|
||||
|
||||
#define ENABLE_WEB_CRYPTO 1
|
||||
#define USE_OPENSSL 1
|
||||
|
||||
20
src/bun.js/bindings/uv-posix-polyfills-darwin.c
Normal file
20
src/bun.js/bindings/uv-posix-polyfills-darwin.c
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
#include "uv-posix-polyfills.h"
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static uv_once_t once = UV_ONCE_INIT;
|
||||
static mach_timebase_info_data_t timebase;
|
||||
|
||||
static void uv__hrtime_init_once(void)
|
||||
{
|
||||
if (KERN_SUCCESS != mach_timebase_info(&timebase))
|
||||
abort();
|
||||
}
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type)
|
||||
{
|
||||
uv_once(&once, uv__hrtime_init_once);
|
||||
return mach_continuous_time() * timebase.numer / timebase.denom;
|
||||
}
|
||||
42
src/bun.js/bindings/uv-posix-polyfills-linux.c
Normal file
42
src/bun.js/bindings/uv-posix-polyfills-linux.c
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
#include "uv-posix-polyfills.h"
|
||||
#include <stdatomic.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type)
|
||||
{
|
||||
static _Atomic clock_t fast_clock_id = -1;
|
||||
struct timespec t;
|
||||
clock_t clock_id;
|
||||
|
||||
/* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
|
||||
* millisecond granularity or better. CLOCK_MONOTONIC_COARSE is
|
||||
* serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
|
||||
* decide to make a costly system call.
|
||||
*/
|
||||
/* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
|
||||
* when it has microsecond granularity or better (unlikely).
|
||||
*/
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
if (type != UV_CLOCK_FAST)
|
||||
goto done;
|
||||
|
||||
clock_id = atomic_load_explicit(&fast_clock_id, memory_order_relaxed);
|
||||
if (clock_id != -1)
|
||||
goto done;
|
||||
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t))
|
||||
if (t.tv_nsec <= 1 * 1000 * 1000)
|
||||
clock_id = CLOCK_MONOTONIC_COARSE;
|
||||
|
||||
atomic_store_explicit(&fast_clock_id, clock_id, memory_order_relaxed);
|
||||
|
||||
done:
|
||||
|
||||
if (clock_gettime(clock_id, &t))
|
||||
return 0; /* Not really possible. */
|
||||
|
||||
return t.tv_sec * (uint64_t)1e9 + t.tv_nsec;
|
||||
}
|
||||
15
src/bun.js/bindings/uv-posix-polyfills-posix.c
Normal file
15
src/bun.js/bindings/uv-posix-polyfills-posix.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "uv-posix-polyfills.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type)
|
||||
{
|
||||
struct timespec t;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &t))
|
||||
abort();
|
||||
|
||||
return t.tv_sec * (uint64_t)1e9 + t.tv_nsec;
|
||||
}
|
||||
141
src/bun.js/bindings/uv-posix-polyfills.c
Normal file
141
src/bun.js/bindings/uv-posix-polyfills.c
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "uv-posix-polyfills.h"
|
||||
|
||||
#if OS(LINUX) || OS(DARWIN)
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// libuv does the annoying thing of #undef'ing these
|
||||
#include <errno.h>
|
||||
#if EDOM > 0
|
||||
#define UV__ERR(x) (-(x))
|
||||
#else
|
||||
#define UV__ERR(x) (x)
|
||||
#endif
|
||||
|
||||
void __bun_throw_not_implemented(const char* symbol_name)
|
||||
{
|
||||
CrashHandler__unsupportedUVFunction(symbol_name);
|
||||
}
|
||||
|
||||
// Internals
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type);
|
||||
|
||||
#if defined(__linux__)
|
||||
#include "uv-posix-polyfills-linux.c"
|
||||
// #elif defined(__MVS__)
|
||||
// #include "uv/os390.h"
|
||||
// #elif defined(__PASE__) /* __PASE__ and _AIX are both defined on IBM i */
|
||||
// #include "uv/posix.h" /* IBM i needs uv/posix.h, not uv/aix.h */
|
||||
// #elif defined(_AIX)
|
||||
// #include "uv/aix.h"
|
||||
// #elif defined(__sun)
|
||||
// #include "uv/sunos.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "uv-posix-polyfills-darwin.c"
|
||||
// #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
// #include "uv/bsd.h"
|
||||
#elif defined(__CYGWIN__) || defined(__MSYS__) || defined(__HAIKU__) || defined(__QNX__) || defined(__GNU__)
|
||||
#include "uv-posix-polyfills-posix.c"
|
||||
#endif
|
||||
|
||||
uv_pid_t uv_os_getpid()
|
||||
{
|
||||
return getpid();
|
||||
}
|
||||
|
||||
uv_pid_t uv_os_getppid()
|
||||
{
|
||||
return getppid();
|
||||
}
|
||||
|
||||
UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void))
|
||||
{
|
||||
if (pthread_once(guard, callback))
|
||||
abort();
|
||||
}
|
||||
|
||||
UV_EXTERN uint64_t uv_hrtime(void)
|
||||
{
|
||||
return uv__hrtime(UV_CLOCK_PRECISE);
|
||||
}
|
||||
|
||||
// Copy-pasted from libuv
|
||||
UV_EXTERN void uv_mutex_destroy(uv_mutex_t* mutex)
|
||||
{
|
||||
if (pthread_mutex_destroy(mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
// Copy-pasted from libuv
|
||||
UV_EXTERN int uv_mutex_init(uv_mutex_t* mutex)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
int err;
|
||||
|
||||
if (pthread_mutexattr_init(&attr))
|
||||
abort();
|
||||
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK))
|
||||
abort();
|
||||
|
||||
err = pthread_mutex_init(mutex, &attr);
|
||||
|
||||
if (pthread_mutexattr_destroy(&attr))
|
||||
abort();
|
||||
|
||||
return UV__ERR(err);
|
||||
}
|
||||
|
||||
// Copy-pasted from libuv
|
||||
UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* mutex)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
int err;
|
||||
|
||||
if (pthread_mutexattr_init(&attr))
|
||||
abort();
|
||||
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE))
|
||||
abort();
|
||||
|
||||
err = pthread_mutex_init(mutex, &attr);
|
||||
|
||||
if (pthread_mutexattr_destroy(&attr))
|
||||
abort();
|
||||
|
||||
return UV__ERR(err);
|
||||
}
|
||||
|
||||
// Copy-pasted from libuv
|
||||
UV_EXTERN void uv_mutex_lock(uv_mutex_t* mutex)
|
||||
{
|
||||
if (pthread_mutex_lock(mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
// Copy-pasted from libuv
|
||||
UV_EXTERN int uv_mutex_trylock(uv_mutex_t* mutex)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_trylock(mutex);
|
||||
if (err) {
|
||||
if (err != EBUSY && err != EAGAIN)
|
||||
abort();
|
||||
return UV_EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy-pasted from libuv
|
||||
UV_EXTERN void uv_mutex_unlock(uv_mutex_t* mutex)
|
||||
{
|
||||
if (pthread_mutex_unlock(mutex))
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
#include "uv-posix-polyfills.h"
|
||||
|
||||
#if OS(LINUX) || OS(DARWIN)
|
||||
|
||||
void __bun_throw_not_implemented(const char* symbol_name)
|
||||
{
|
||||
CrashHandler__unsupportedUVFunction(symbol_name);
|
||||
}
|
||||
|
||||
uv_pid_t uv_os_getpid()
|
||||
{
|
||||
return getpid();
|
||||
}
|
||||
|
||||
uv_pid_t uv_os_getppid()
|
||||
{
|
||||
return getppid();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -8,12 +8,17 @@
|
||||
|
||||
// These functions are called by the stubs to crash with a nice error message
|
||||
// when accessing a libuv functin which we do not support on posix
|
||||
extern "C" void CrashHandler__unsupportedUVFunction(const char* function_name);
|
||||
void CrashHandler__unsupportedUVFunction(const char* function_name);
|
||||
void __bun_throw_not_implemented(const char* symbol_name);
|
||||
|
||||
// libuv headers will use UV_EXTERN
|
||||
#define UV_EXTERN extern "C" __attribute__((visibility("default"))) __attribute__((used))
|
||||
#define UV_EXTERN __attribute__((visibility("default"))) __attribute__((used))
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
typedef enum {
|
||||
UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */
|
||||
UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */
|
||||
} uv_clocktype_t;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -830,12 +830,6 @@ UV_EXTERN int uv_has_ref(const uv_handle_t*)
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN uint64_t uv_hrtime(void)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_hrtime");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN int uv_idle_init(uv_loop_t*, uv_idle_t* idle)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_idle_init");
|
||||
@@ -1063,54 +1057,12 @@ UV_EXTERN int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics)
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_mutex_destroy");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN int uv_mutex_init(uv_mutex_t* handle)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_mutex_init");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_mutex_init_recursive");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_mutex_lock");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_mutex_trylock");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_mutex_unlock");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN uint64_t uv_now(const uv_loop_t*)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_now");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void))
|
||||
{
|
||||
__bun_throw_not_implemented("uv_once");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd)
|
||||
{
|
||||
__bun_throw_not_implemented("uv_open_osfhandle");
|
||||
@@ -449,6 +449,7 @@ pub const Run = struct {
|
||||
}
|
||||
|
||||
JSC.napi.fixDeadCodeElimination();
|
||||
bun.crash_handler.fixDeadCodeElimination();
|
||||
vm.globalExit();
|
||||
}
|
||||
|
||||
|
||||
@@ -255,11 +255,12 @@ pub fn crashHandler(
|
||||
\\Bun is actively working on supporting all libuv functions for POSIX
|
||||
\\systems, please see this issue to track our progress:
|
||||
\\
|
||||
\\<cyan>https://github.com/oven-sh/bun/issues/4290<r>
|
||||
\\<cyan>https://github.com/oven-sh/bun/issues/18546<r>
|
||||
\\
|
||||
\\
|
||||
;
|
||||
writer.print(Output.prettyFmt(fmt, true), .{name}) catch std.posix.abort();
|
||||
has_printed_message = true;
|
||||
}
|
||||
} else {
|
||||
if (Output.enable_ansi_colors) {
|
||||
@@ -368,7 +369,7 @@ pub fn crashHandler(
|
||||
\\Bun is actively working on supporting all libuv functions for POSIX
|
||||
\\systems, please see this issue to track our progress:
|
||||
\\
|
||||
\\<cyan>https://github.com/oven-sh/bun/issues/4290<r>
|
||||
\\<cyan>https://github.com/oven-sh/bun/issues/18546<r>
|
||||
\\
|
||||
\\
|
||||
;
|
||||
@@ -1862,7 +1863,7 @@ export fn CrashHandler__setInsideNativePlugin(name: ?[*:0]const u8) callconv(.C)
|
||||
inside_native_plugin = name;
|
||||
}
|
||||
|
||||
fn unsupportUVFunction(name: ?[*:0]const u8) callconv(.C) void {
|
||||
export fn CrashHandler__unsupportedUVFunction(name: ?[*:0]const u8) callconv(.C) void {
|
||||
bun.analytics.Features.unsupported_uv_function += 1;
|
||||
unsupported_uv_function = name;
|
||||
std.debug.panic("unsupported uv function: {s}", .{name.?});
|
||||
@@ -1882,9 +1883,13 @@ export fn CrashHandler__setDlOpenAction(action: ?[*:0]const u8) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fixDeadCodeElimination() void {
|
||||
std.mem.doNotOptimizeAway(&CrashHandler__unsupportedUVFunction);
|
||||
}
|
||||
comptime {
|
||||
_ = &Bun__crashHandler;
|
||||
if (!bun.Environment.isWindows) {
|
||||
@export(&unsupportUVFunction, .{ .name = "CrashHandler__unsupportedUVFunction" });
|
||||
std.mem.doNotOptimizeAway(&CrashHandler__unsupportedUVFunction);
|
||||
// @export(&unsupportUVFunction, .{ .name = "CrashHandler__unsupportedUVFunction", .linkage = .strong });
|
||||
}
|
||||
}
|
||||
|
||||
159
test/napi/uv-stub-stuff/uv_impl.c
Normal file
159
test/napi/uv-stub-stuff/uv_impl.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#include <node_api.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <uv.h>
|
||||
|
||||
// Test mutex initialization and destruction
|
||||
static napi_value test_mutex_init_destroy(napi_env env,
|
||||
napi_callback_info info) {
|
||||
uv_mutex_t mutex;
|
||||
int result = uv_mutex_init(&mutex);
|
||||
if (result != 0) {
|
||||
napi_throw_error(env, NULL, "Failed to initialize mutex");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uv_mutex_destroy(&mutex);
|
||||
|
||||
napi_value ret;
|
||||
napi_get_boolean(env, true, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Test recursive mutex
|
||||
static napi_value test_mutex_recursive(napi_env env, napi_callback_info info) {
|
||||
uv_mutex_t mutex;
|
||||
int result = uv_mutex_init_recursive(&mutex);
|
||||
if (result != 0) {
|
||||
napi_throw_error(env, NULL, "Failed to initialize recursive mutex");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Try locking multiple times
|
||||
uv_mutex_lock(&mutex);
|
||||
uv_mutex_lock(&mutex);
|
||||
|
||||
// Unlock the same number of times
|
||||
uv_mutex_unlock(&mutex);
|
||||
uv_mutex_unlock(&mutex);
|
||||
|
||||
uv_mutex_destroy(&mutex);
|
||||
|
||||
napi_value ret;
|
||||
napi_get_boolean(env, true, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Test mutex trylock
|
||||
static napi_value test_mutex_trylock(napi_env env, napi_callback_info info) {
|
||||
uv_mutex_t mutex;
|
||||
uv_mutex_init(&mutex);
|
||||
|
||||
int result = uv_mutex_trylock(&mutex);
|
||||
if (result != 0) {
|
||||
uv_mutex_destroy(&mutex);
|
||||
napi_throw_error(env, NULL, "Failed to trylock mutex");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uv_mutex_unlock(&mutex);
|
||||
uv_mutex_destroy(&mutex);
|
||||
|
||||
napi_value ret;
|
||||
napi_get_boolean(env, true, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Test getpid and getppid
|
||||
static napi_value test_process_ids(napi_env env, napi_callback_info info) {
|
||||
uv_pid_t pid = uv_os_getpid();
|
||||
uv_pid_t ppid = uv_os_getppid();
|
||||
|
||||
// Create return object with pid and ppid
|
||||
napi_value obj;
|
||||
napi_create_object(env, &obj);
|
||||
|
||||
napi_value pid_value, ppid_value;
|
||||
napi_create_int32(env, pid, &pid_value);
|
||||
napi_create_int32(env, ppid, &ppid_value);
|
||||
|
||||
napi_set_named_property(env, obj, "pid", pid_value);
|
||||
napi_set_named_property(env, obj, "ppid", ppid_value);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
// Test uv_once
|
||||
static void once_callback(void) {
|
||||
// Just a dummy callback
|
||||
count++;
|
||||
}
|
||||
uv_once_t guard = UV_ONCE_INIT;
|
||||
|
||||
static napi_value test_uv_once(napi_env env, napi_callback_info info) {
|
||||
uv_once(&guard, once_callback);
|
||||
|
||||
napi_value ret;
|
||||
napi_create_int32(env, count, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Test uv_hrtime
|
||||
static napi_value test_hrtime(napi_env env, napi_callback_info info) {
|
||||
uint64_t time1 = uv_hrtime();
|
||||
|
||||
// Sleep for a tiny bit to ensure time passes
|
||||
usleep(1000); // Sleep for 1ms
|
||||
|
||||
uint64_t time2 = uv_hrtime();
|
||||
|
||||
// Create return object with both timestamps
|
||||
napi_value obj;
|
||||
napi_create_object(env, &obj);
|
||||
|
||||
// Convert uint64_t to two int32 values (high and low bits)
|
||||
// because JavaScript numbers can't safely handle 64-bit integers
|
||||
napi_value time1_low, time1_high, time2_low, time2_high;
|
||||
napi_create_int32(env, (int32_t)(time1 & 0xFFFFFFFF), &time1_low);
|
||||
napi_create_int32(env, (int32_t)(time1 >> 32), &time1_high);
|
||||
napi_create_int32(env, (int32_t)(time2 & 0xFFFFFFFF), &time2_low);
|
||||
napi_create_int32(env, (int32_t)(time2 >> 32), &time2_high);
|
||||
|
||||
napi_set_named_property(env, obj, "time1Low", time1_low);
|
||||
napi_set_named_property(env, obj, "time1High", time1_high);
|
||||
napi_set_named_property(env, obj, "time2Low", time2_low);
|
||||
napi_set_named_property(env, obj, "time2High", time2_high);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
napi_value Init(napi_env env, napi_value exports) {
|
||||
// Register all test functions
|
||||
napi_value fn;
|
||||
|
||||
napi_create_function(env, NULL, 0, test_mutex_init_destroy, NULL, &fn);
|
||||
napi_set_named_property(env, exports, "testMutexInitDestroy", fn);
|
||||
|
||||
napi_create_function(env, NULL, 0, test_mutex_recursive, NULL, &fn);
|
||||
napi_set_named_property(env, exports, "testMutexRecursive", fn);
|
||||
|
||||
napi_create_function(env, NULL, 0, test_mutex_trylock, NULL, &fn);
|
||||
napi_set_named_property(env, exports, "testMutexTrylock", fn);
|
||||
|
||||
napi_create_function(env, NULL, 0, test_process_ids, NULL, &fn);
|
||||
napi_set_named_property(env, exports, "testProcessIds", fn);
|
||||
|
||||
napi_create_function(env, NULL, 0, test_uv_once, NULL, &fn);
|
||||
napi_set_named_property(env, exports, "testUvOnce", fn);
|
||||
|
||||
napi_create_function(env, NULL, 0, test_hrtime, NULL, &fn);
|
||||
napi_set_named_property(env, exports, "testHrtime", fn);
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
||||
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|
||||
114
test/napi/uv.test.ts
Normal file
114
test/napi/uv.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { beforeAll, describe, expect, afterEach, test } from "bun:test";
|
||||
import path from "node:path";
|
||||
import { bunEnv, bunExe, makeTree, tempDirWithFiles, isWindows } from "harness";
|
||||
import source from "./uv-stub-stuff/uv_impl.c";
|
||||
import { symbols, test_skipped } from "../../src/bun.js/bindings/libuv/generate_uv_posix_stubs_constants";
|
||||
|
||||
const symbols_to_test = symbols.filter(s => !test_skipped.includes(s));
|
||||
|
||||
// We use libuv on Windows
|
||||
describe.if(!isWindows)("uv stubs", () => {
|
||||
const cwd = process.cwd();
|
||||
let tempdir: string = "";
|
||||
let outdir: string = "";
|
||||
let nativeModule: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const files = {
|
||||
"uv_impl.c": await Bun.file(source).text(),
|
||||
"package.json": JSON.stringify({
|
||||
"name": "fake-plugin",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0",
|
||||
},
|
||||
"scripts": {
|
||||
"build:napi": "node-gyp configure && node-gyp build",
|
||||
},
|
||||
"dependencies": {
|
||||
"node-gyp": "10.2.0",
|
||||
},
|
||||
}),
|
||||
"binding.gyp": `{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "uv_test",
|
||||
"sources": [ "uv_impl.c" ],
|
||||
"include_dirs": [ ".", "./libuv" ],
|
||||
"cflags": ["-fPIC"],
|
||||
"ldflags": ["-Wl,--export-dynamic"]
|
||||
},
|
||||
]
|
||||
}`,
|
||||
};
|
||||
|
||||
tempdir = tempDirWithFiles("uv-tests", files);
|
||||
await makeTree(tempdir, files);
|
||||
outdir = path.join(tempdir, "dist");
|
||||
|
||||
process.chdir(tempdir);
|
||||
|
||||
const libuvDir = path.join(__dirname, "../../src/bun.js/bindings/libuv");
|
||||
await Bun.$`cp -R ${libuvDir} ${path.join(tempdir, "libuv")}`;
|
||||
await Bun.$`${bunExe()} i && ${bunExe()} build:napi`.env(bunEnv).cwd(tempdir);
|
||||
|
||||
nativeModule = require(path.join(tempdir, "./build/Release/uv_test.node"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.chdir(cwd);
|
||||
});
|
||||
|
||||
test("mutex init and destroy", () => {
|
||||
expect(() => nativeModule.testMutexInitDestroy()).not.toThrow();
|
||||
});
|
||||
|
||||
test("recursive mutex", () => {
|
||||
expect(() => nativeModule.testMutexRecursive()).not.toThrow();
|
||||
});
|
||||
|
||||
test("mutex trylock", () => {
|
||||
expect(() => nativeModule.testMutexTrylock()).not.toThrow();
|
||||
});
|
||||
|
||||
test("process IDs", () => {
|
||||
const result = nativeModule.testProcessIds();
|
||||
expect(result).toHaveProperty("pid");
|
||||
expect(result).toHaveProperty("ppid");
|
||||
expect(result.pid).toBeGreaterThan(0);
|
||||
expect(result.ppid).toBeGreaterThan(0);
|
||||
// The process ID should match Node's process.pid
|
||||
expect(result.pid).toBe(process.pid);
|
||||
});
|
||||
|
||||
test("uv_once", () => {
|
||||
expect(nativeModule.testUvOnce()).toBe(1);
|
||||
expect(nativeModule.testUvOnce()).toBe(1);
|
||||
expect(nativeModule.testUvOnce()).toBe(1);
|
||||
});
|
||||
|
||||
test("hrtime", () => {
|
||||
const result = nativeModule.testHrtime();
|
||||
|
||||
// Reconstruct the 64-bit values
|
||||
const time1 = (BigInt(result.time1High) << 32n) | BigInt(result.time1Low >>> 0);
|
||||
const time2 = (BigInt(result.time2High) << 32n) | BigInt(result.time2Low >>> 0);
|
||||
|
||||
// Verify that:
|
||||
// 1. time2 is greater than time1 (time passed)
|
||||
expect(time2 > time1).toBe(true);
|
||||
|
||||
// 2. The difference should be at least 1ms (we slept for 1ms)
|
||||
// hrtime is in nanoseconds, so 1ms = 1,000,000 ns
|
||||
const diff = time2 - time1;
|
||||
expect(diff >= 1_000_000n).toBe(true);
|
||||
|
||||
// 3. The difference shouldn't be unreasonably large
|
||||
// Let's say not more than 100ms (100,000,000 ns)
|
||||
expect(diff <= 100_000_000n).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user