Skip to content

Commit

Permalink
Merge pull request #1173 from ziglang/event-loop-channel
Browse files Browse the repository at this point in the history
add event loop Channel abstraction
  • Loading branch information
andrewrk committed Jul 2, 2018
2 parents 3546352 + 2da9993 commit 22b7312
Show file tree
Hide file tree
Showing 6 changed files with 465 additions and 52 deletions.
37 changes: 34 additions & 3 deletions src-self-hosted/main.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const builtin = @import("builtin");

const event = std.event;
const os = std.os;
const io = std.io;
const mem = std.mem;
Expand Down Expand Up @@ -43,6 +44,9 @@ const Command = struct {
};

pub fn main() !void {
// This allocator needs to be thread-safe because we use it for the event.Loop
// which multiplexes coroutines onto kernel threads.
// libc allocator is guaranteed to have this property.
const allocator = std.heap.c_allocator;

var stdout_file = try std.io.getStdOut();
Expand Down Expand Up @@ -380,8 +384,10 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
defer allocator.free(zig_lib_dir);

var loop = try event.Loop.init(allocator);

var module = try Module.create(
allocator,
&loop,
root_name,
root_source_file,
Target.Native,
Expand Down Expand Up @@ -471,9 +477,35 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
module.emit_file_type = emit_type;
module.link_objects = link_objects;
module.assembly_files = assembly_files;
module.link_out_file = flags.single("out-file");

try module.build();
try module.link(flags.single("out-file"));
const process_build_events_handle = try async<loop.allocator> processBuildEvents(module, true);
defer cancel process_build_events_handle;
loop.run();
}

async fn processBuildEvents(module: *Module, watch: bool) void {
while (watch) {
// TODO directly awaiting async should guarantee memory allocation elision
const build_event = await (async module.events.get() catch unreachable);

switch (build_event) {
Module.Event.Ok => {
std.debug.warn("Build succeeded\n");
// for now we stop after 1
module.loop.stop();
return;
},
Module.Event.Error => |err| {
std.debug.warn("build failed: {}\n", @errorName(err));
@panic("TODO error return trace");
},
Module.Event.Fail => |errs| {
@panic("TODO print compile error messages");
},
}
}
}

fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void {
Expand Down Expand Up @@ -780,4 +812,3 @@ const CliPkg = struct {
self.children.deinit();
}
};

136 changes: 101 additions & 35 deletions src-self-hosted/module.zig
Expand Up @@ -11,9 +11,11 @@ const warn = std.debug.warn;
const Token = std.zig.Token;
const ArrayList = std.ArrayList;
const errmsg = @import("errmsg.zig");
const ast = std.zig.ast;
const event = std.event;

pub const Module = struct {
allocator: *mem.Allocator,
loop: *event.Loop,
name: Buffer,
root_src_path: ?[]const u8,
module: llvm.ModuleRef,
Expand Down Expand Up @@ -76,6 +78,51 @@ pub const Module = struct {

kind: Kind,

link_out_file: ?[]const u8,
events: *event.Channel(Event),

// TODO handle some of these earlier and report them in a way other than error codes
pub const BuildError = error{
OutOfMemory,
EndOfStream,
BadFd,
Io,
IsDir,
Unexpected,
SystemResources,
SharingViolation,
PathAlreadyExists,
FileNotFound,
AccessDenied,
PipeBusy,
FileTooBig,
SymLinkLoop,
ProcessFdQuotaExceeded,
NameTooLong,
SystemFdQuotaExceeded,
NoDevice,
PathNotFound,
NoSpaceLeft,
NotDir,
FileSystem,
OperationAborted,
IoPending,
BrokenPipe,
WouldBlock,
FileClosed,
DestinationAddressRequired,
DiskQuota,
InputOutput,
NoStdHandles,
Overflow,
};

pub const Event = union(enum) {
Ok,
Fail: []errmsg.Msg,
Error: BuildError,
};

pub const DarwinVersionMin = union(enum) {
None,
MacOS: []const u8,
Expand Down Expand Up @@ -104,7 +151,7 @@ pub const Module = struct {
};

pub fn create(
allocator: *mem.Allocator,
loop: *event.Loop,
name: []const u8,
root_src_path: ?[]const u8,
target: *const Target,
Expand All @@ -113,7 +160,7 @@ pub const Module = struct {
zig_lib_dir: []const u8,
cache_dir: []const u8,
) !*Module {
var name_buffer = try Buffer.init(allocator, name);
var name_buffer = try Buffer.init(loop.allocator, name);
errdefer name_buffer.deinit();

const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
Expand All @@ -125,8 +172,12 @@ pub const Module = struct {
const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
errdefer c.LLVMDisposeBuilder(builder);

const module_ptr = try allocator.create(Module{
.allocator = allocator,
const events = try event.Channel(Event).create(loop, 0);
errdefer events.destroy();

return loop.allocator.create(Module{
.loop = loop,
.events = events,
.name = name_buffer,
.root_src_path = root_src_path,
.module = module,
Expand Down Expand Up @@ -171,76 +222,87 @@ pub const Module = struct {
.link_objects = [][]const u8{},
.windows_subsystem_windows = false,
.windows_subsystem_console = false,
.link_libs_list = ArrayList(*LinkLib).init(allocator),
.link_libs_list = ArrayList(*LinkLib).init(loop.allocator),
.libc_link_lib = null,
.err_color = errmsg.Color.Auto,
.darwin_frameworks = [][]const u8{},
.darwin_version_min = DarwinVersionMin.None,
.test_filters = [][]const u8{},
.test_name_prefix = null,
.emit_file_type = Emit.Binary,
.link_out_file = null,
});
errdefer allocator.destroy(module_ptr);
return module_ptr;
}

fn dump(self: *Module) void {
c.LLVMDumpModule(self.module);
}

pub fn destroy(self: *Module) void {
self.events.destroy();
c.LLVMDisposeBuilder(self.builder);
c.LLVMDisposeModule(self.module);
c.LLVMContextDispose(self.context);
self.name.deinit();

self.allocator.destroy(self);
self.a().destroy(self);
}

pub fn build(self: *Module) !void {
if (self.llvm_argv.len != 0) {
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator, [][]const []const u8{
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{
[][]const u8{"zig (LLVM option parsing)"},
self.llvm_argv,
});
defer c_compatible_args.deinit();
// TODO this sets global state
c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
}

_ = try async<self.a()> self.buildAsync();
}

async fn buildAsync(self: *Module) void {
while (true) {
// TODO directly awaiting async should guarantee memory allocation elision
// TODO also async before suspending should guarantee memory allocation elision
(await (async self.addRootSrc() catch unreachable)) catch |err| {
await (async self.events.put(Event{ .Error = err }) catch unreachable);
return;
};
await (async self.events.put(Event.Ok) catch unreachable);
}
}

async fn addRootSrc(self: *Module) !void {
const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {
const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
try printError("unable to get real path '{}': {}", root_src_path, err);
return err;
};
errdefer self.allocator.free(root_src_real_path);
errdefer self.a().free(root_src_real_path);

const source_code = io.readFileAlloc(self.allocator, root_src_real_path) catch |err| {
const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
try printError("unable to open '{}': {}", root_src_real_path, err);
return err;
};
errdefer self.allocator.free(source_code);

warn("====input:====\n");

warn("{}", source_code);
errdefer self.a().free(source_code);

warn("====parse:====\n");

var tree = try std.zig.parse(self.allocator, source_code);
var tree = try std.zig.parse(self.a(), source_code);
defer tree.deinit();

var stderr_file = try std.io.getStdErr();
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
const out_stream = &stderr_file_out_stream.stream;

warn("====fmt:====\n");
_ = try std.zig.render(self.allocator, out_stream, &tree);

warn("====ir:====\n");
warn("TODO\n\n");

warn("====llvm ir:====\n");
self.dump();
//var it = tree.root_node.decls.iterator();
//while (it.next()) |decl_ptr| {
// const decl = decl_ptr.*;
// switch (decl.id) {
// ast.Node.Comptime => @panic("TODO"),
// ast.Node.VarDecl => @panic("TODO"),
// ast.Node.UseDecl => @panic("TODO"),
// ast.Node.FnDef => @panic("TODO"),
// ast.Node.TestDecl => @panic("TODO"),
// else => unreachable,
// }
//}
}

pub fn link(self: *Module, out_file: ?[]const u8) !void {
Expand All @@ -263,18 +325,22 @@ pub const Module = struct {
}
}

const link_lib = try self.allocator.create(LinkLib{
const link_lib = try self.a().create(LinkLib{
.name = name,
.path = null,
.provided_explicitly = provided_explicitly,
.symbols = ArrayList([]u8).init(self.allocator),
.symbols = ArrayList([]u8).init(self.a()),
});
try self.link_libs_list.append(link_lib);
if (is_libc) {
self.libc_link_lib = link_lib;
}
return link_lib;
}

fn a(self: Module) *mem.Allocator {
return self.loop.allocator;
}
};

fn printError(comptime format: []const u8, args: ...) !void {
Expand Down
2 changes: 1 addition & 1 deletion std/atomic/queue_mpsc.zig
@@ -1,4 +1,4 @@
const std = @import("std");
const std = @import("../index.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
const AtomicOrder = builtin.AtomicOrder;
Expand Down

0 comments on commit 22b7312

Please sign in to comment.