aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-05-04 23:56:03 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-05-05 21:35:08 -0700
commitc80e048ab3d2c89eb663757b4641655d12124885 (patch)
tree9ce0836dfa01116f9bb6c40c3e40aeee201303d3
parentb6a8675658bab8eb9be9950a8de3fdeb9421aee7 (diff)
downloadbun-c80e048ab3d2c89eb663757b4641655d12124885.tar.gz
bun-c80e048ab3d2c89eb663757b4641655d12124885.tar.zst
bun-c80e048ab3d2c89eb663757b4641655d12124885.zip
implement napi_async_work
Diffstat (limited to '')
-rw-r--r--src/javascript/jsc/base.zig1
-rw-r--r--src/javascript/jsc/javascript.zig53
-rw-r--r--src/napi/napi.zig136
3 files changed, 175 insertions, 15 deletions
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index ac2d05106..1664eca17 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -2604,7 +2604,6 @@ const SHA256 = JSC.API.Bun.Crypto.SHA256;
const SHA512_256 = JSC.API.Bun.Crypto.SHA512_256;
const MD5_SHA1 = JSC.API.Bun.Crypto.MD5_SHA1;
const FFI = JSC.FFI;
-
pub const JSPrivateDataPtr = TaggedPointerUnion(.{
AttributeIterator,
BigIntStats,
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 894b7bea5..06f0c47a1 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -296,11 +296,57 @@ pub fn IOTask(comptime Context: type) type {
};
}
+pub fn AsyncNativeCallbackTask(comptime Context: type) type {
+ return struct {
+ const This = @This();
+ ctx: *Context,
+ task: WorkPoolTask = .{ .callback = runFromThreadPool },
+ event_loop: *VirtualMachine.EventLoop,
+ allocator: std.mem.Allocator,
+ globalThis: *JSGlobalObject,
+
+ pub fn createOnJSThread(allocator: std.mem.Allocator, globalThis: *JSGlobalObject, value: *Context) !*This {
+ var this = try allocator.create(This);
+ this.* = .{
+ .event_loop = VirtualMachine.vm.eventLoop(),
+ .ctx = value,
+ .allocator = allocator,
+ .globalThis = globalThis,
+ };
+ return this;
+ }
+
+ pub fn runFromThreadPool(task: *WorkPoolTask) void {
+ var this = @fieldParentPtr(This, "task", task);
+ Context.run(this.ctx, this);
+ }
+
+ pub fn runFromJS(this: This) void {
+ this.ctx.runFromJS(this.globalThis);
+ }
+
+ pub fn schedule(this: *This) void {
+ WorkPool.get().schedule(WorkPool.schedule(&this.task));
+ }
+
+ pub fn onFinish(this: *This) void {
+ this.event_loop.enqueueTaskConcurrent(Task.init(this));
+ }
+
+ pub fn deinit(this: *This) void {
+ var allocator = this.allocator;
+ this.* = undefined;
+ allocator.destroy(this);
+ }
+ };
+}
+
const CopyFilePromiseTask = WebCore.Blob.Store.CopyFile.CopyFilePromiseTask;
const AsyncTransformTask = @import("./api/transpiler.zig").TransformTask.AsyncTransformTask;
const BunTimerTimeoutTask = Bun.Timer.Timeout.TimeoutTask;
const ReadFileTask = WebCore.Blob.Store.ReadFile.ReadFileTask;
const WriteFileTask = WebCore.Blob.Store.WriteFile.WriteFileTask;
+const napi_async_work = JSC.napi.napi_async_work;
// const PromiseTask = JSInternalPromise.Completion.PromiseTask;
pub const Task = TaggedPointerUnion(.{
FetchTasklet,
@@ -311,6 +357,7 @@ pub const Task = TaggedPointerUnion(.{
CopyFilePromiseTask,
WriteFileTask,
AnyTask,
+ napi_async_work,
// PromiseTask,
// TimeoutTasklet,
});
@@ -579,6 +626,12 @@ pub const VirtualMachine = struct {
finished += 1;
vm_.active_tasks -|= 1;
},
+ @field(Task.Tag, @typeName(JSC.napi.napi_async_work)) => {
+ var transform_task: *JSC.napi.napi_async_work = task.get(JSC.napi.napi_async_work).?;
+ transform_task.*.runFromJS();
+ finished += 1;
+ vm_.active_tasks -|= 1;
+ },
@field(Task.Tag, @typeName(BunTimerTimeoutTask)) => {
var transform_task: *BunTimerTimeoutTask = task.get(BunTimerTimeoutTask).?;
transform_task.*.runFromJS();
diff --git a/src/napi/napi.zig b/src/napi/napi.zig
index 2af4da303..74973806a 100644
--- a/src/napi/napi.zig
+++ b/src/napi/napi.zig
@@ -12,11 +12,6 @@ pub const napi_handle_scope = napi_env;
pub const napi_escapable_handle_scope = struct_napi_escapable_handle_scope__;
pub const napi_callback_info = *JSC.CallFrame;
pub const napi_deferred = *JSC.JSPromise;
-pub const napi_callback_scope = struct_napi_callback_scope__;
-pub const napi_async_context = struct_napi_async_context__;
-pub const napi_async_work = struct_napi_async_work__;
-pub const napi_threadsafe_function = struct_napi_threadsafe_function__;
-pub const napi_async_cleanup_hook_handle = struct_napi_async_cleanup_hook_handle__;
pub const uv_loop_s = struct_uv_loop_s;
pub const napi_value = JSC.JSValue;
@@ -363,6 +358,7 @@ pub export fn napi_typeof(env: napi_env, value: napi_value, result: *napi_valuet
}
return .invalid_arg;
+}
pub export fn napi_get_value_double(_: napi_env, value: napi_value, result: *f64) napi_status {
result.* = value.to(f64);
return .ok;
@@ -868,12 +864,97 @@ pub extern fn napi_type_tag_object(env: napi_env, value: napi_value, type_tag: [
pub extern fn napi_check_object_type_tag(env: napi_env, value: napi_value, type_tag: [*c]const napi_type_tag, result: *bool) napi_status;
pub extern fn napi_object_freeze(env: napi_env, object: napi_value) napi_status;
pub extern fn napi_object_seal(env: napi_env, object: napi_value) napi_status;
-pub const struct_napi_callback_scope__ = opaque {};
-pub const napi_callback_scope = ?*struct_napi_callback_scope__;
-pub const struct_napi_async_context__ = opaque {};
-pub const napi_async_context = ?*struct_napi_async_context__;
pub const struct_napi_async_work__ = opaque {};
-pub const napi_async_work = ?*struct_napi_async_work__;
+const WorkPool = @import("../work_pool.zig").WorkPool;
+
+/// must be globally allocated
+pub const napi_async_work = struct {
+ task: JSC.WorkPoolTask = .{ .callback = runFromThreadPool },
+ completion_task: ?*anyopaque = null,
+ event_loop: *JSC.VirtualMachine.EventLoop,
+ global: napi_env,
+ execute: napi_async_execute_callback = null,
+ complete: napi_async_complete_callback = null,
+ ctx: ?*anyopaque = null,
+ status: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(0),
+ can_deinit: bool = false,
+ wait_for_deinit: bool = false,
+ scheduled: bool = false,
+ pub const Status = enum(u32) {
+ pending = 0,
+ started = 1,
+ completed = 2,
+ cancelled = 3,
+ };
+
+ pub fn create(global: napi_env, execute: napi_async_execute_callback, complete: napi_async_complete_callback, ctx: ?*anyopaque) !*napi_async_work {
+ var work = try bun.default_allocator.create(napi_async_work);
+ work.* = .{
+ .global = global,
+ .execute = execute,
+ .complete = complete,
+ .ctx = ctx,
+ };
+ return work;
+ }
+
+ pub fn runFromThreadPool(task: *JSC.WorkPoolTask) void {
+ var this = @fieldParentPtr(napi_async_work, "task", task);
+
+ this.run();
+ }
+ pub fn run(this: *napi_async_work) void {
+ if (this.status.compareAndSwap(@enumToInt(Status.pending), @enumToInt(Status.started), .SeqCst, .SeqCst)) |state| {
+ if (state == @enumToInt(Status.cancelled)) {
+ if (this.wait_for_deinit) {
+ // this might cause a segfault due to Task using a linked list!
+ bun.default_allocator.destroy(this);
+ }
+ }
+ return;
+ }
+ this.execute.?(this.global, this.ctx);
+ this.status.store(@enumToInt(Status.completed), .SeqCst);
+
+ this.event_loop.enqueueTaskConcurrent(JSC.Task.from(JSC.Task.init(this)));
+ }
+
+ pub fn schedule(this: *napi_async_work) void {
+ if (this.scheduled) return;
+ this.scheduled = true;
+ WorkPool.get().schedule(&this.task);
+ }
+
+ pub fn cancel(this: *napi_async_work) bool {
+ const prev_status = @intToEnum(
+ Status,
+ this.status.compareAndSwap(@enumToInt(Status.cancelled), @enumToInt(Status.pending), .SeqCst, .SeqCst),
+ );
+ if (prev_status == Status.pending) {
+ return true;
+ }
+ return false;
+ }
+
+ pub fn deinit(this: *napi_async_work) void {
+ if (this.can_deinit) {
+ bun.default_allocator.destroy(this);
+ return;
+ }
+ this.wait_for_deinit = true;
+ }
+
+ pub fn runFromJS(this: *napi_async_work) void {
+ this.complete.?(
+ this.global,
+ if (this.status.load(.SeqCst) == @enumToInt(Status.cancelled))
+ napi_status.cancelled
+ else
+ napi_status.ok,
+ this.ctx,
+ );
+ }
+};
pub const struct_napi_threadsafe_function__ = opaque {};
pub const napi_threadsafe_function = ?*struct_napi_threadsafe_function__;
pub const napi_tsfn_release: c_int = 0;
@@ -961,10 +1042,37 @@ pub export fn napi_get_buffer_info(env: napi_env, value: napi_value, data: *[*]u
length.* = array_buf.length;
return .ok;
}
-pub extern fn napi_create_async_work(env: napi_env, async_resource: napi_value, async_resource_name: napi_value, execute: napi_async_execute_callback, complete: napi_async_complete_callback, data: ?*anyopaque, result: [*c]napi_async_work) napi_status;
-pub extern fn napi_delete_async_work(env: napi_env, work: napi_async_work) napi_status;
-pub extern fn napi_queue_async_work(env: napi_env, work: napi_async_work) napi_status;
-pub extern fn napi_cancel_async_work(env: napi_env, work: napi_async_work) napi_status;
+pub export fn napi_create_async_work(
+ env: napi_env,
+ _: napi_value,
+ _: [*:0]const u8,
+ execute: napi_async_execute_callback,
+ complete: napi_async_complete_callback,
+ data: ?*anyopaque,
+ result: *napi_async_work,
+) napi_status {
+ result.* = napi_async_work.create(env, execute, complete, data) catch {
+ return .generic_failure;
+ };
+ return .ok;
+}
+pub export fn napi_delete_async_work(env: napi_env, work: *napi_async_work) napi_status {
+ std.debug.assert(env == work.global);
+ work.deinit();
+}
+pub export fn napi_queue_async_work(env: napi_env, work: *napi_async_work) napi_status {
+ std.debug.assert(env == work.global);
+ work.schedule();
+ return .ok;
+}
+pub export fn napi_cancel_async_work(env: napi_env, work: *napi_async_work) napi_status {
+ std.debug.assert(env == work.global);
+ if (work.cancel()) {
+ return .ok;
+ }
+
+ return napi_status.generic_failure;
+}
pub export fn napi_get_node_version(_: napi_env, version: **const napi_node_version) napi_status {
version.* = &napi_node_version.global;
return .ok;