Commit 951124e177

Andrew Kelley <superjoe30@gmail.com>
2018-08-02 23:24:15
evented I/O zig fmt
1 parent 821805a
Changed files (1)
src-self-hosted
src-self-hosted/main.zig
@@ -527,33 +527,12 @@ const args_fmt_spec = []Flag{
 };
 
 const Fmt = struct {
-    seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),
-    queue: std.LinkedList([]const u8),
+    seen: event.Locked(SeenMap),
     any_error: bool,
+    color: errmsg.Color,
+    loop: *event.Loop,
 
-    // file_path must outlive Fmt
-    fn addToQueue(self: *Fmt, file_path: []const u8) !void {
-        const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{
-            .prev = undefined,
-            .next = undefined,
-            .data = file_path,
-        });
-
-        if (try self.seen.put(file_path, {})) |_| return;
-
-        self.queue.append(new_node);
-    }
-
-    fn addDirToQueue(self: *Fmt, file_path: []const u8) !void {
-        var dir = try std.os.Dir.open(self.seen.allocator, file_path);
-        defer dir.close();
-        while (try dir.next()) |entry| {
-            if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
-                const full_path = try os.path.join(self.seen.allocator, file_path, entry.name);
-                try self.addToQueue(full_path);
-            }
-        }
-    }
+    const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
 };
 
 fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
@@ -664,66 +643,144 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
         os.exit(1);
     }
 
+    var loop: event.Loop = undefined;
+    try loop.initMultiThreaded(allocator);
+    defer loop.deinit();
+
+    var result: FmtError!void = undefined;
+    const main_handle = try async<allocator> asyncFmtMainChecked(
+        &result,
+        &loop,
+        flags,
+        color,
+    );
+    defer cancel main_handle;
+    loop.run();
+    return result;
+}
+
+async fn asyncFmtMainChecked(
+    result: *(FmtError!void),
+    loop: *event.Loop,
+    flags: *const Args,
+    color: errmsg.Color,
+) void {
+    result.* = await (async asyncFmtMain(loop, flags, color) catch unreachable);
+}
+
+const FmtError = error{
+    SystemResources,
+    OperationAborted,
+    IoPending,
+    BrokenPipe,
+    Unexpected,
+    WouldBlock,
+    FileClosed,
+    DestinationAddressRequired,
+    DiskQuota,
+    FileTooBig,
+    InputOutput,
+    NoSpaceLeft,
+    AccessDenied,
+    OutOfMemory,
+    RenameAcrossMountPoints,
+    ReadOnlyFileSystem,
+    LinkQuotaExceeded,
+    FileBusy,
+} || os.File.OpenError;
+
+async fn asyncFmtMain(
+    loop: *event.Loop,
+    flags: *const Args,
+    color: errmsg.Color,
+) FmtError!void {
+    suspend |p| {
+        resume p;
+    }
+    // Things we need to make event-based:
+    // * opening the file in the first place - the open()
+    // * read()
+    // * readdir()
+    // * the actual parsing and rendering
+    // * rename()
     var fmt = Fmt{
-        .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),
-        .queue = std.LinkedList([]const u8).init(),
+        .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
         .any_error = false,
+        .color = color,
+        .loop = loop,
     };
 
+    var group = event.Group(FmtError!void).init(loop);
     for (flags.positionals.toSliceConst()) |file_path| {
-        try fmt.addToQueue(file_path);
+        try group.call(fmtPath, &fmt, file_path);
     }
+    return await (async group.wait() catch unreachable);
+}
 
-    while (fmt.queue.popFirst()) |node| {
-        const file_path = node.data;
+async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
+    const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref);
+    defer fmt.loop.allocator.free(file_path);
 
-        var file = try os.File.openRead(allocator, file_path);
-        defer file.close();
+    {
+        const held = await (async fmt.seen.acquire() catch unreachable);
+        defer held.release();
 
-        const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) {
-            error.IsDir => {
-                try fmt.addDirToQueue(file_path);
-                continue;
-            },
-            else => {
-                try stderr.print("unable to open '{}': {}\n", file_path, err);
-                fmt.any_error = true;
-                continue;
-            },
-        };
-        defer allocator.free(source_code);
+        if (try held.value.put(file_path, {})) |_| return;
+    }
 
-        var tree = std.zig.parse(allocator, source_code) catch |err| {
-            try stderr.print("error parsing file '{}': {}\n", file_path, err);
+    const source_code = (await try async event.fs.readFile(
+        fmt.loop,
+        file_path,
+        2 * 1024 * 1024 * 1024,
+    )) catch |err| switch (err) {
+        error.IsDir => {
+            var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
+            defer dir.close();
+
+            var group = event.Group(FmtError!void).init(fmt.loop);
+            while (try dir.next()) |entry| {
+                if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
+                    const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name);
+                    try group.call(fmtPath, fmt, full_path);
+                }
+            }
+            return await (async group.wait() catch unreachable);
+        },
+        else => {
+            // TODO lock stderr printing
+            try stderr.print("unable to open '{}': {}\n", file_path, err);
             fmt.any_error = true;
-            continue;
-        };
-        defer tree.deinit();
-
-        var error_it = tree.errors.iterator(0);
-        while (error_it.next()) |parse_error| {
-            const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
-            defer msg.destroy();
+            return;
+        },
+    };
+    defer fmt.loop.allocator.free(source_code);
 
-            try msg.printToFile(&stderr_file, color);
-        }
-        if (tree.errors.len != 0) {
-            fmt.any_error = true;
-            continue;
-        }
+    var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
+        try stderr.print("error parsing file '{}': {}\n", file_path, err);
+        fmt.any_error = true;
+        return;
+    };
+    defer tree.deinit();
 
-        const baf = try io.BufferedAtomicFile.create(allocator, file_path);
-        defer baf.destroy();
+    var error_it = tree.errors.iterator(0);
+    while (error_it.next()) |parse_error| {
+        const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
+        defer fmt.loop.allocator.destroy(msg);
 
-        const anything_changed = try std.zig.render(allocator, baf.stream(), &tree);
-        if (anything_changed) {
-            try stderr.print("{}\n", file_path);
-            try baf.finish();
-        }
+        try msg.printToFile(&stderr_file, fmt.color);
+    }
+    if (tree.errors.len != 0) {
+        fmt.any_error = true;
+        return;
     }
 
-    if (fmt.any_error) {
-        os.exit(1);
+    const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
+    defer baf.destroy();
+
+    const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree);
+    if (anything_changed) {
+        try stderr.print("{}\n", file_path);
+        try baf.finish();
     }
 }