Skip to content
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

feat: Add cp builtin to shell for Windows #10174

Merged
merged 55 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
e0eca4f
Initial impl
zackradisic Apr 10, 2024
da35efc
more stuff
zackradisic Apr 11, 2024
70f8b81
Make it work on windows
zackradisic Apr 11, 2024
2482924
Merge branch 'main' into zack/shell-cp
zackradisic Apr 11, 2024
0036299
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 11, 2024
03d5402
remove vtable
zackradisic Apr 11, 2024
692d42f
Merge branch 'main' into zack/shell-cp
zackradisic Apr 11, 2024
529e3fc
Add way to enable these on posix
Jarred-Sumner Apr 12, 2024
a0469d7
Merge branch 'main' into zack/shell-cp
Jarred-Sumner Apr 12, 2024
303ba67
Lint
Jarred-Sumner Apr 12, 2024
492b4fc
Make AsyncCpTask a comptime fn and have two variations
zackradisic Apr 15, 2024
f2373b9
clean up
zackradisic Apr 11, 2024
848ccad
Make mini work
zackradisic Apr 15, 2024
4a57456
Fix mini event loop crash and allow mini event loop to be tested in T…
zackradisic Apr 15, 2024
29213ba
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 15, 2024
d6b83cf
yoops
zackradisic Apr 15, 2024
5d4e3a3
make get runtime feature flag atomic
zackradisic Apr 15, 2024
0293ba5
fix tests
zackradisic Apr 15, 2024
14b725f
fix tests again
zackradisic Apr 15, 2024
4950554
fix tests again for real this time
zackradisic Apr 15, 2024
28390fd
Merge branch 'main' into zack/shell-cp
Jarred-Sumner Apr 16, 2024
c0daac0
Merge branch 'main' into zack/shell-cp
zackradisic Apr 16, 2024
52fecf9
Fix merge problems
zackradisic Apr 16, 2024
e4d505e
Merge branch 'main' into zack/shell-cp
zackradisic Apr 22, 2024
ba60d79
Merge branch 'main' into zack/shell-cp
Jarred-Sumner Apr 23, 2024
45b693c
Apply formatting changes
Jarred-Sumner Apr 23, 2024
7ce2e78
Merge branch 'main' into zack/shell-cp
zackradisic Apr 23, 2024
9dddc13
Merge branch 'main' into zack/shell-cp
Jarred-Sumner Apr 24, 2024
081e2aa
Fix EBUSY problem
zackradisic Apr 24, 2024
45a62c8
accidentally comitted some debug stuff
zackradisic Apr 24, 2024
f29b8f3
Apply formatting changes
zackradisic Apr 24, 2024
db7ed85
Merge branch 'main' into zack/shell-cp
zackradisic Apr 24, 2024
a6cc409
change `print` -> `debug`
zackradisic Apr 25, 2024
a7e5c8f
resolve comments
zackradisic Apr 25, 2024
72563fd
Apply formatting changes
zackradisic Apr 25, 2024
fe4e317
resolve some comments
zackradisic Apr 25, 2024
d4d59ca
Merge branch 'main' into zack/shell-cp
zackradisic Apr 25, 2024
b9fa0a4
woops
zackradisic Apr 25, 2024
d5f62c6
Apply formatting changes
zackradisic Apr 25, 2024
0b56f81
Fix
zackradisic Apr 25, 2024
eccd2ab
yoops
zackradisic Apr 25, 2024
b5c8a3a
remove debug stuff
zackradisic Apr 25, 2024
f842d69
deleted stuff by accident
zackradisic Apr 25, 2024
d5547ef
Apply formatting changes
zackradisic Apr 25, 2024
6ab458e
Fix hang
zackradisic Apr 26, 2024
d29b95d
Apply formatting changes
zackradisic Apr 26, 2024
4b59b0e
Merge branch 'main' into zack/shell-cp
zackradisic Apr 26, 2024
efeb53c
Merge branch 'main' into zack/shell-cp
zackradisic Apr 29, 2024
52d6ac0
Merge branch 'main' into zack/shell-cp
zackradisic Apr 29, 2024
26852b6
Merge branch 'main' into zack/shell-cp
zackradisic Apr 30, 2024
8d79986
Merge branch 'main' into zack/shell-cp
zackradisic May 1, 2024
c6fc84d
Merge branch 'main' into zack/shell-cp
zackradisic May 8, 2024
5974622
Fix lint
zackradisic May 8, 2024
36727bf
resolve comments
zackradisic May 8, 2024
ec8eabe
add comment
zackradisic May 8, 2024
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
49 changes: 43 additions & 6 deletions src/bun.js/event_loop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,14 @@ pub const AnyTaskWithExtraContext = struct {
callback: *const (fn (*anyopaque, *anyopaque) void) = undefined,
next: ?*AnyTaskWithExtraContext = null,

pub fn fromCallbackAutoDeinit(of: anytype, comptime callback: anytype) *AnyTaskWithExtraContext {
const TheTask = NewManaged(std.meta.Child(@TypeOf(of)), void, @field(std.meta.Child(@TypeOf(of)), callback));
const task = bun.default_allocator.create(AnyTaskWithExtraContext) catch bun.outOfMemory();
task.* = TheTask.init(of);
return task;
}

pub fn from(this: *@This(), of: anytype, comptime field: []const u8) *@This() {
// this.* = .{
// .ctx = of,
// .callback = @field(std.meta.Child(@TypeOf(of)), field),
// .next = null,
// };
// return this;
const TheTask = New(std.meta.Child(@TypeOf(of)), void, @field(std.meta.Child(@TypeOf(of)), field));
this.* = TheTask.init(of);
return this;
Expand Down Expand Up @@ -266,6 +267,29 @@ pub const AnyTaskWithExtraContext = struct {
}
};
}

pub fn NewManaged(comptime Type: type, comptime ContextType: type, comptime Callback: anytype) type {
return struct {
pub fn init(ctx: *Type) AnyTaskWithExtraContext {
return AnyTaskWithExtraContext{
.callback = wrap,
.ctx = ctx,
};
}

pub fn wrap(this: ?*anyopaque, extra: ?*anyopaque) void {
@call(
.always_inline,
Callback,
.{
@as(*Type, @ptrCast(@alignCast(this.?))),
@as(*ContextType, @ptrCast(@alignCast(extra.?))),
},
);
bun.default_allocator.destroy(@as(*AnyTaskWithExtraContext, @ptrCast(@alignCast(this.?))));
}
};
}
};

pub const CppTask = opaque {
Expand Down Expand Up @@ -360,6 +384,7 @@ const ShellMvCheckTargetTask = bun.shell.Interpreter.Builtin.Mv.ShellMvCheckTarg
const ShellMvBatchedTask = bun.shell.Interpreter.Builtin.Mv.ShellMvBatchedTask;
const ShellMkdirTask = bun.shell.Interpreter.Builtin.Mkdir.ShellMkdirTask;
const ShellTouchTask = bun.shell.Interpreter.Builtin.Touch.ShellTouchTask;
const ShellCpTask = bun.shell.Interpreter.Builtin.Cp.ShellCpTask;
const ShellCondExprStatTask = bun.shell.Interpreter.CondExpr.ShellCondExprStatTask;
const ShellAsync = bun.shell.Interpreter.Async;
// const ShellIOReaderAsyncDeinit = bun.shell.Interpreter.IOReader.AsyncDeinit;
Expand Down Expand Up @@ -438,6 +463,7 @@ pub const Task = TaggedPointerUnion(.{
ShellLsTask,
ShellMkdirTask,
ShellTouchTask,
ShellCpTask,
ShellCondExprStatTask,
ShellAsync,
ShellAsyncSubprocessDone,
Expand Down Expand Up @@ -913,6 +939,10 @@ pub const EventLoop = struct {
var shell_ls_task: *ShellCondExprStatTask = task.get(ShellCondExprStatTask).?;
shell_ls_task.task.runFromMainThread();
},
@field(Task.Tag, typeBaseName(@typeName(ShellCpTask))) => {
var shell_ls_task: *ShellCpTask = task.get(ShellCpTask).?;
shell_ls_task.runFromMainThread();
},
@field(Task.Tag, typeBaseName(@typeName(ShellTouchTask))) => {
var shell_ls_task: *ShellTouchTask = task.get(ShellTouchTask).?;
shell_ls_task.runFromMainThread();
Expand Down Expand Up @@ -2081,6 +2111,13 @@ pub const EventLoopHandle = union(enum) {
js: *JSC.EventLoop,
mini: *MiniEventLoop,

pub fn globalObject(this: EventLoopHandle) ?*JSC.JSGlobalObject {
return switch (this) {
.js => this.js.global,
.mini => null,
};
}

pub fn stdout(this: EventLoopHandle) *JSC.WebCore.Blob.Store {
return switch (this) {
.js => this.js.virtual_machine.rareData().stdout(),
Expand Down
104 changes: 93 additions & 11 deletions src/bun.js/node/node_fs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,9 @@ pub const Async = struct {
};

pub const AsyncCpTask = struct {
zackradisic marked this conversation as resolved.
Show resolved Hide resolved
promise: JSC.JSPromise.Strong,
promise: JSC.JSPromise.Strong = .{},
args: Arguments.Cp,
globalObject: *JSC.JSGlobalObject,
evtloop: JSC.EventLoopHandle,
task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
result: JSC.Maybe(Return.Cp),
ref: bun.Async.KeepAlive = .{},
Expand All @@ -257,23 +257,50 @@ pub const AsyncCpTask = struct {
/// The maintask thread starts this at 1 and decrements it at the end, to avoid the promise being resolved while new tasks may be added.
subtask_count: std.atomic.Value(usize),

shelltask: ?*ShellTask = null,

const ShellTask = bun.shell.Interpreter.Builtin.Cp.ShellCpTask;

pub fn onCopy(this: *AsyncCpTask, src: anytype, dest: anytype) void {
const task = this.shelltask orelse return;
task.cpOnCopy(src, dest);
}

pub fn onFinish(this: *AsyncCpTask, result: Maybe(void)) void {
const task = this.shelltask orelse return;
task.cpOnFinish(result);
}

pub fn create(
globalObject: *JSC.JSGlobalObject,
cp_args: Arguments.Cp,
vm: *JSC.VirtualMachine,
arena: bun.ArenaAllocator,
) JSC.JSValue {
const task = createWithShellTask(globalObject, cp_args, vm, arena, null, true);
return task.promise.value();
}

pub fn createWithShellTask(
globalObject: *JSC.JSGlobalObject,
cp_args: Arguments.Cp,
vm: *JSC.VirtualMachine,
arena: bun.ArenaAllocator,
shelltask: ?*ShellTask,
comptime enable_promise: bool,
) *AsyncCpTask {
var task = bun.new(
AsyncCpTask,
AsyncCpTask{
.promise = JSC.JSPromise.Strong.init(globalObject),
.promise = if (comptime enable_promise) JSC.JSPromise.Strong.init(globalObject) else .{},
.args = cp_args,
.has_result = .{ .raw = false },
.result = undefined,
.globalObject = globalObject,
.evtloop = .{ .js = vm.event_loop },
.tracker = JSC.AsyncTaskTracker.init(vm),
.arena = arena,
.subtask_count = .{ .raw = 1 },
.shelltask = shelltask,
},
);
task.ref.ref(vm);
Expand All @@ -283,7 +310,35 @@ pub const AsyncCpTask = struct {

JSC.WorkPool.schedule(&task.task);

return task.promise.value();
return task;
}

pub fn createMini(
cp_args: Arguments.Cp,
arena: bun.ArenaAllocator,
shelltask: *ShellTask,
) *AsyncCpTask {
var task = bun.new(
AsyncCpTask,
AsyncCpTask{
.args = cp_args,
.has_result = .{ .raw = false },
.result = undefined,
.evtloop = .{ .mini = JSC.MiniEventLoop.global },
.tracker = JSC.AsyncTaskTracker{ .id = 0 },
.arena = arena,
.subtask_count = .{ .raw = 1 },
.shelltask = shelltask,
},
);
task.ref.ref(JSC.MiniEventLoop.global);
task.args.src.toThreadSafe();
task.args.dest.toThreadSafe();
// task.tracker.didSchedule(globalObject);

JSC.WorkPool.schedule(&task.task);

return task;
}

fn workPoolCallback(task: *JSC.WorkPoolTask) void {
Expand All @@ -305,11 +360,27 @@ pub const AsyncCpTask = struct {
this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch "";
}

this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread));
if (this.evtloop == .js) {
this.evtloop.enqueueTaskConcurrent(.{ .js = JSC.ConcurrentTask.fromCallback(this, runFromJSThread) });
} else {
// this.evtloop.enqueueTaskConcurrent(.{ .mini = JSC.AnyTaskWithExtraContext.fromCallbackAutoDeinit(this, "runFromJSThreadMini") });
this.evtloop.enqueueTaskConcurrent(.{ .mini = JSC.AnyTaskWithExtraContext.fromCallbackAutoDeinit(this, "runFromJSThreadMini") });
}
}

pub fn runFromJSThreadMini(this: *AsyncCpTask, _: *void) void {
this.runFromJSThread();
}

fn runFromJSThread(this: *AsyncCpTask) void {
const globalObject = this.globalObject;
if (this.shelltask) |shelltask| {
shelltask.cpOnFinish(this.result);
this.deinit();
return;
}
const globalObject = this.evtloop.globalObject() orelse {
@panic("No global object, this indicates a bug in Bun. Please file a GitHub issue.");
};
var success = @as(JSC.Maybe(Return.Cp).Tag, this.result) == .result;
const result = switch (this.result) {
.err => |err| err.toJSC(globalObject),
Expand Down Expand Up @@ -340,7 +411,7 @@ pub const AsyncCpTask = struct {
}

pub fn deinit(this: *AsyncCpTask) void {
this.ref.unref(this.globalObject.bunVM());
this.ref.unref(this.evtloop);
this.args.deinit();
this.promise.strong.deinit();
this.arena.deinit();
Expand Down Expand Up @@ -732,7 +803,9 @@ pub const AsyncCpSingleFileTask = struct {
this.deinit();
return;
},
.result => {},
.result => {
this.cp_task.onCopy(this.src, this.dest);
},
}
}

Expand Down Expand Up @@ -6531,13 +6604,18 @@ pub const NodeFS = struct {
const r = this._copySingleFileSync(
src,
dest,
@enumFromInt((if (args.flags.errorOnExist or !args.flags.force) Constants.COPYFILE_EXCL else @as(u8, 0))),
if (task.shelltask != null)
// Shell always forces copy
@enumFromInt(Constants.Copyfile.force)
else
@enumFromInt((if (args.flags.errorOnExist or !args.flags.force) Constants.COPYFILE_EXCL else @as(u8, 0))),
attributes,
);
if (r == .err and r.err.errno == @intFromEnum(E.EXIST) and !args.flags.errorOnExist) {
task.finishConcurrently(Maybe(Return.Cp).success);
return;
}
task.onCopy(src, dest);
task.finishConcurrently(r);
return;
}
Expand All @@ -6560,9 +6638,11 @@ pub const NodeFS = struct {
stat_,
);
if (r == .err and r.err.errno == @intFromEnum(E.EXIST) and !args.flags.errorOnExist) {
task.onCopy(src, dest);
task.finishConcurrently(Maybe(Return.Cp).success);
return;
}
task.onCopy(src, dest);
task.finishConcurrently(r);
return;
}
Expand Down Expand Up @@ -6648,7 +6728,9 @@ pub const NodeFS = struct {
task.finishConcurrently(.{ .err = err });
return false;
},
.result => {},
.result => {
task.onCopy(src, normdest);
},
}

const dir = fd.asDir();
Expand Down
16 changes: 16 additions & 0 deletions src/bun.zig
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,22 @@ pub fn openDirAbsolute(path_: []const u8) !std.fs.Dir {
}
}
pub const MimallocArena = @import("./mimalloc_arena.zig").Arena;
pub fn getRuntimeFeatureFlag(comptime flag: [:0]const u8) bool {
return struct {
const flag_ = flag;
var is_enabled: ?bool = null;
pub fn get() bool {
return is_enabled orelse brk: {
is_enabled = false;
if (getenvZ(flag_)) |val| {
is_enabled = strings.eqlComptime(val, "1") or strings.eqlComptime(val, "true");
}

break :brk is_enabled.?;
};
}
}.get();
}
zackradisic marked this conversation as resolved.
Show resolved Hide resolved

/// This wrapper exists to avoid the call to sliceTo(0)
/// Zig's sliceTo(0) is scalar
Expand Down
9 changes: 9 additions & 0 deletions src/js/internal-for-testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,13 @@ export const SQL = $cpp("JSSQLStatement.cpp", "createJSSQLStatementConstructor")
export const shellInternals = {
lex: $newZigFunction("shell.zig", "TestingAPIs.shellLex", 1),
parse: $newZigFunction("shell.zig", "TestingAPIs.shellParse", 1),
/**
* Checks if the given builtin is disabled on the current platform
*
* @example
* ```typescript
* const isDisabled = builtinDisabled("cp")
* ```
*/
builtinDisabled: $newZigFunction("shell.zig", "TestingAPIs.disabledOnThisPlatform", 1),
};
Loading
Loading