diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index a849be80ba..e300680015 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -359,6 +359,7 @@ const ShellMkdirTask = bun.shell.Interpreter.Builtin.Mkdir.ShellMkdirTask; const ShellTouchTask = bun.shell.Interpreter.Builtin.Touch.ShellTouchTask; // const ShellIOReaderAsyncDeinit = bun.shell.Interpreter.IOReader.AsyncDeinit; const ShellIOReaderAsyncDeinit = bun.shell.Interpreter.AsyncDeinit; +const ShellIOWriterAsyncDeinit = bun.shell.Interpreter.AsyncDeinitWriter; const TimerReference = JSC.BunTimer.Timeout.TimerReference; const ProcessWaiterThreadTask = if (Environment.isPosix) bun.spawn.WaiterThread.ProcessQueue.ResultTask else opaque {}; const ProcessMiniEventLoopWaiterThreadTask = if (Environment.isPosix) bun.spawn.WaiterThread.ProcessMiniEventLoopQueue.ResultTask else opaque {}; @@ -373,6 +374,7 @@ pub const Task = TaggedPointerUnion(.{ AnyTask, ManagedTask, ShellIOReaderAsyncDeinit, + ShellIOWriterAsyncDeinit, napi_async_work, ThreadSafeFunction, CppTask, @@ -880,6 +882,11 @@ pub const EventLoop = struct { while (@field(this, queue_name).readItem()) |task| { defer counter += 1; switch (task.tag()) { + @field(Task.Tag, typeBaseName(@typeName(ShellIOWriterAsyncDeinit))) => { + var shell_ls_task: *ShellIOWriterAsyncDeinit = task.get(ShellIOWriterAsyncDeinit).?; + shell_ls_task.runFromMainThread(); + // shell_ls_task.deinit(); + }, @field(Task.Tag, typeBaseName(@typeName(ShellIOReaderAsyncDeinit))) => { var shell_ls_task: *ShellIOReaderAsyncDeinit = task.get(ShellIOReaderAsyncDeinit).?; shell_ls_task.runFromMainThread(); @@ -1691,11 +1698,13 @@ pub const MiniEventLoop = struct { pipe_read_buffer: ?*PipeReadBuffer = null, const PipeReadBuffer = [256 * 1024]u8; + pub threadlocal var globalInitialized: bool = false; pub threadlocal var global: *MiniEventLoop = undefined; pub const ConcurrentTaskQueue = UnboundedQueue(AnyTaskWithExtraContext, .next); pub fn initGlobal(env: ?*bun.DotEnv.Loader) *MiniEventLoop { + if (globalInitialized) return global; const loop = MiniEventLoop.init(bun.default_allocator); global = bun.default_allocator.create(MiniEventLoop) catch bun.outOfMemory(); global.* = loop; @@ -1707,6 +1716,7 @@ pub const MiniEventLoop = struct { loader.* = bun.DotEnv.Loader.init(map, bun.default_allocator); break :env_loader loader; }; + globalInitialized = true; return global; } @@ -1750,11 +1760,17 @@ pub const MiniEventLoop = struct { pub fn init( allocator: std.mem.Allocator, ) MiniEventLoop { - return .{ + var mini = MiniEventLoop{ .tasks = Queue.init(allocator), .allocator = allocator, .loop = uws.Loop.get(), }; + + if (comptime Environment.isWindows) { + mini.loop.uv_loop = bun.windows.libuv.Loop.get(); + } + + return mini; } pub fn deinit(this: *MiniEventLoop) void { diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index b761ade80c..0f6ed38364 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -305,26 +305,25 @@ pub const RunCommand = struct { } if (Environment.isWindows and !use_native_shell) { - @panic("TODO: Windows shell support"); - // if (!silent) { - // if (Environment.isDebug) { - // Output.prettyError("[bun shell] ", .{}); - // } - // Output.prettyErrorln("$ {s}", .{combined_script}); - // Output.flush(); - // } + if (!silent) { + if (Environment.isDebug) { + Output.prettyError("[bun shell] ", .{}); + } + Output.prettyErrorln("$ {s}", .{combined_script}); + Output.flush(); + } - // const mini = bun.JSC.MiniEventLoop.initGlobal(env); - // bun.shell.ShellSubprocessMini.initAndRunFromSource(mini, name, combined_script) catch |err| { - // if (!silent) { - // Output.prettyErrorln("error: Failed to run script {s} due to error {s}", .{ name, @errorName(err) }); - // } + const mini = bun.JSC.MiniEventLoop.initGlobal(env); + bun.shell.Interpreter.initAndRunFromSource(mini, name, combined_script) catch |err| { + if (!silent) { + Output.prettyErrorln("error: Failed to run script {s} due to error {s}", .{ name, @errorName(err) }); + } - // Output.flush(); - // Global.exit(1); - // }; + Output.flush(); + Global.exit(1); + }; - // return true; + return true; } var argv = [_]string{ diff --git a/src/shell/interpreter.zig b/src/shell/interpreter.zig index ba94cc0a1d..b91af8c17e 100644 --- a/src/shell/interpreter.zig +++ b/src/shell/interpreter.zig @@ -1220,7 +1220,7 @@ pub const Interpreter = struct { }; const script_heap = try arena.allocator().create(ast.Script); script_heap.* = script; - var interp = switch (ThisInterpreter.init(mini, bun.default_allocator, &arena, script_heap, jsobjs)) { + var interp = switch (ThisInterpreter.init(.{ .mini = mini }, bun.default_allocator, &arena, script_heap, jsobjs)) { .err => |*e| { throwShellErr(e, .{ .mini = mini }); return; @@ -1239,6 +1239,7 @@ pub const Interpreter = struct { interp.done = &is_done.done; try interp.run(); mini.tick(&is_done, @as(fn (*anyopaque) bool, IsDone.isDone)); + interp.deinit(); } pub fn run(this: *ThisInterpreter) !void { @@ -8737,6 +8738,37 @@ pub const Interpreter = struct { pub const Readers = SmolList(ChildPtr, 4); }; + pub const AsyncDeinitWriter = struct { + task: WorkPoolTask = .{ .callback = &runFromThreadPool }, + + pub fn runFromThreadPool(task: *WorkPoolTask) void { + var this = @fieldParentPtr(@This(), "task", task); + var iowriter = this.writer(); + if (iowriter.evtloop == .js) { + iowriter.evtloop.js.enqueueTaskConcurrent(iowriter.concurrent_task.js.from(this, .manual_deinit)); + } else { + iowriter.evtloop.mini.enqueueTaskConcurrent(iowriter.concurrent_task.mini.from(this, "runFromMainThreadMini")); + } + } + + pub fn writer(this: *@This()) *IOWriter { + return @fieldParentPtr(IOWriter, "async_deinit", this); + } + + pub fn runFromMainThread(this: *@This()) void { + const ioreader = @fieldParentPtr(IOWriter, "async_deinit", this); + ioreader.__deinit(); + } + + pub fn runFromMainThreadMini(this: *@This(), _: *void) void { + this.runFromMainThread(); + } + + pub fn schedule(this: *@This()) void { + WorkPool.schedule(&this.task); + } + }; + pub const AsyncDeinit = struct { task: WorkPoolTask = .{ .callback = &runFromThreadPool }, @@ -8780,7 +8812,9 @@ pub const Interpreter = struct { ref_count: u32 = 1, err: ?JSC.SystemError = null, evtloop: JSC.EventLoopHandle, + concurrent_task: JSC.EventLoopTask, is_writing: if (bun.Environment.isWindows) bool else u0 = if (bun.Environment.isWindows) false else 0, + async_deinit: AsyncDeinitWriter = .{}, pub const DEBUG_REFCOUNT_NAME: []const u8 = "IOWriterRefCount"; @@ -8796,7 +8830,7 @@ pub const Interpreter = struct { pub const auto_poll = false; - usingnamespace bun.NewRefCounted(@This(), This.deinit); + usingnamespace bun.NewRefCounted(@This(), asyncDeinit); const This = @This(); pub const WriterImpl = bun.io.BufferedWriter( This, @@ -8820,6 +8854,7 @@ pub const Interpreter = struct { const this = IOWriter.new(.{ .fd = fd, .evtloop = evtloop, + .concurrent_task = JSC.EventLoopTask.fromEventLoop(evtloop), }); this.writer.parent = this; @@ -9083,7 +9118,11 @@ pub const Interpreter = struct { this.write(); } - pub fn deinit(this: *This) void { + pub fn asyncDeinit(this: *@This()) void { + this.async_deinit.schedule(); + } + + pub fn __deinit(this: *This) void { print("IOWriter(0x{x}, fd={}) deinit", .{ @intFromPtr(this), this.fd }); if (bun.Environment.allow_assert) std.debug.assert(this.ref_count == 0); this.buf.deinit(bun.default_allocator);