| author | |
| committer | |
| log | 9fb4d1fd6c521e1bc3b656315b9c693a9e8aa715 |
| tree | e5500f9bcf0d0d857e4531a7b9873c64684f61a2 |
| parent | 9dfaf3166d161e125d70fac7bca5aab1ad02625b |
using signal handlers10 files changed, 384 insertions(+), 134 deletions(-)
src/ir.cpp+22| ... | ... | @@ -11513,6 +11513,28 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru |
| 11513 | 11513 | buf_ptr(&child_type->name), buf_ptr(field_name))); |
| 11514 | 11514 | return ira->codegen->builtin_types.entry_invalid; |
| 11515 | 11515 | } |
| 11516 | } else if (child_type->id == TypeTableEntryIdArray) { | |
| 11517 | if (buf_eql_str(field_name, "child")) { | |
| 11518 | bool ptr_is_const = true; | |
| 11519 | bool ptr_is_volatile = false; | |
| 11520 | return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, | |
| 11521 | create_const_type(ira->codegen, child_type->data.array.child_type), | |
| 11522 | ira->codegen->builtin_types.entry_type, | |
| 11523 | ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); | |
| 11524 | } else if (buf_eql_str(field_name, "len")) { | |
| 11525 | bool ptr_is_const = true; | |
| 11526 | bool ptr_is_volatile = false; | |
| 11527 | return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, | |
| 11528 | create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int, | |
| 11529 | child_type->data.array.len, false), | |
| 11530 | ira->codegen->builtin_types.entry_num_lit_int, | |
| 11531 | ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); | |
| 11532 | } else { | |
| 11533 | ir_add_error(ira, &field_ptr_instruction->base, | |
| 11534 | buf_sprintf("type '%s' has no member called '%s'", | |
| 11535 | buf_ptr(&child_type->name), buf_ptr(field_name))); | |
| 11536 | return ira->codegen->builtin_types.entry_invalid; | |
| 11537 | } | |
| 11516 | 11538 | } else { |
| 11517 | 11539 | ir_add_error(ira, &field_ptr_instruction->base, |
| 11518 | 11540 | buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); |
std/build.zig+2-2| ... | ... | @@ -545,7 +545,7 @@ pub const Builder = struct { |
| 545 | 545 | } |
| 546 | 546 | |
| 547 | 547 | var child = os.ChildProcess.spawn(exe_path, args, cwd, env_map, |
| 548 | StdIo.Inherit, StdIo.Inherit, StdIo.Inherit, self.allocator) %% |err| | |
| 548 | StdIo.Inherit, StdIo.Inherit, StdIo.Inherit, null, self.allocator) %% |err| | |
| 549 | 549 | { |
| 550 | 550 | %%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err)); |
| 551 | 551 | return err; |
| ... | ... | @@ -556,7 +556,7 @@ pub const Builder = struct { |
| 556 | 556 | return err; |
| 557 | 557 | }; |
| 558 | 558 | switch (term) { |
| 559 | Term.Clean => |code| { | |
| 559 | Term.Exited => |code| { | |
| 560 | 560 | if (code != 0) { |
| 561 | 561 | %%io.stderr.printf("Process {} exited with error code {}\n", exe_path, code); |
| 562 | 562 | return error.UncleanExit; |
std/linked_list.zig+35-50| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | const debug = @import("debug.zig"); |
| 2 | 2 | const assert = debug.assert; |
| 3 | 3 | const mem = @import("mem.zig"); |
| 4 | const Allocator = mem.Allocator; | |
| 5 | 4 | |
| 6 | 5 | /// Generic doubly linked list. |
| 7 | 6 | pub fn LinkedList(comptime T: type) -> type { |
| ... | ... | @@ -13,26 +12,29 @@ pub fn LinkedList(comptime T: type) -> type { |
| 13 | 12 | prev: ?&Node, |
| 14 | 13 | next: ?&Node, |
| 15 | 14 | data: T, |
| 15 | ||
| 16 | pub fn init(data: &const T) -> Node { | |
| 17 | Node { | |
| 18 | .data = *data, | |
| 19 | .prev = null, | |
| 20 | .next = null, | |
| 21 | } | |
| 22 | } | |
| 16 | 23 | }; |
| 17 | 24 | |
| 18 | 25 | first: ?&Node, |
| 19 | 26 | last: ?&Node, |
| 20 | 27 | len: usize, |
| 21 | allocator: &Allocator, | |
| 22 | 28 | |
| 23 | 29 | /// Initialize a linked list. |
| 24 | 30 | /// |
| 25 | /// Arguments: | |
| 26 | /// allocator: Dynamic memory allocator. | |
| 27 | /// | |
| 28 | 31 | /// Returns: |
| 29 | 32 | /// An empty linked list. |
| 30 | pub fn init(allocator: &Allocator) -> Self { | |
| 33 | pub fn init() -> Self { | |
| 31 | 34 | Self { |
| 32 | 35 | .first = null, |
| 33 | 36 | .last = null, |
| 34 | 37 | .len = 0, |
| 35 | .allocator = allocator, | |
| 36 | 38 | } |
| 37 | 39 | } |
| 38 | 40 | |
| ... | ... | @@ -155,55 +157,38 @@ pub fn LinkedList(comptime T: type) -> type { |
| 155 | 157 | return first; |
| 156 | 158 | } |
| 157 | 159 | |
| 158 | /// Allocate a new node. | |
| 159 | /// | |
| 160 | /// Returns: | |
| 161 | /// A pointer to the new node. | |
| 162 | pub fn allocateNode(list: &Self) -> %&Node { | |
| 163 | list.allocator.create(Node) | |
| 164 | } | |
| 160 | } | |
| 161 | } | |
| 165 | 162 | |
| 166 | /// Deallocate a node. | |
| 167 | /// | |
| 168 | /// Arguments: | |
| 169 | /// node: Pointer to the node to deallocate. | |
| 170 | pub fn destroyNode(list: &Self, node: &Node) { | |
| 171 | list.allocator.destroy(node); | |
| 172 | } | |
| 163 | pub fn testAllocateNode(comptime T: type, list: &LinkedList(T), allocator: &mem.Allocator) -> %&LinkedList(T).Node { | |
| 164 | allocator.create(LinkedList(T).Node) | |
| 165 | } | |
| 173 | 166 | |
| 174 | /// Allocate and initialize a node and its data. | |
| 175 | /// | |
| 176 | /// Arguments: | |
| 177 | /// data: The data to put inside the node. | |
| 178 | /// | |
| 179 | /// Returns: | |
| 180 | /// A pointer to the new node. | |
| 181 | pub fn createNode(list: &Self, data: &const T) -> %&Node { | |
| 182 | var node = %return list.allocateNode(); | |
| 183 | *node = Node { | |
| 184 | .prev = null, | |
| 185 | .next = null, | |
| 186 | .data = *data, | |
| 187 | }; | |
| 188 | return node; | |
| 189 | } | |
| 190 | } | |
| 167 | pub fn testDestroyNode(comptime T: type, list: &LinkedList(T), node: &LinkedList(T).Node, allocator: &mem.Allocator) { | |
| 168 | allocator.destroy(node); | |
| 191 | 169 | } |
| 192 | 170 | |
| 193 | test "basic linked list test" { | |
| 194 | var list = LinkedList(u32).init(&debug.global_allocator); | |
| 171 | pub fn testCreateNode(comptime T: type, list: &LinkedList(T), data: &const T, allocator: &mem.Allocator) -> %&LinkedList(T).Node { | |
| 172 | var node = %return testAllocateNode(T, list, allocator); | |
| 173 | *node = LinkedList(T).Node.init(data); | |
| 174 | return node; | |
| 175 | } | |
| 195 | 176 | |
| 196 | var one = %%list.createNode(1); | |
| 197 | var two = %%list.createNode(2); | |
| 198 | var three = %%list.createNode(3); | |
| 199 | var four = %%list.createNode(4); | |
| 200 | var five = %%list.createNode(5); | |
| 177 | test "basic linked list test" { | |
| 178 | const allocator = &debug.global_allocator; | |
| 179 | var list = LinkedList(u32).init(); | |
| 180 | ||
| 181 | var one = %%testCreateNode(u32, &list, 1, allocator); | |
| 182 | var two = %%testCreateNode(u32, &list, 2, allocator); | |
| 183 | var three = %%testCreateNode(u32, &list, 3, allocator); | |
| 184 | var four = %%testCreateNode(u32, &list, 4, allocator); | |
| 185 | var five = %%testCreateNode(u32, &list, 5, allocator); | |
| 201 | 186 | defer { |
| 202 | list.destroyNode(one); | |
| 203 | list.destroyNode(two); | |
| 204 | list.destroyNode(three); | |
| 205 | list.destroyNode(four); | |
| 206 | list.destroyNode(five); | |
| 187 | testDestroyNode(u32, &list, one, allocator); | |
| 188 | testDestroyNode(u32, &list, two, allocator); | |
| 189 | testDestroyNode(u32, &list, three, allocator); | |
| 190 | testDestroyNode(u32, &list, four, allocator); | |
| 191 | testDestroyNode(u32, &list, five, allocator); | |
| 207 | 192 | } |
| 208 | 193 | |
| 209 | 194 | list.append(two); // {2} |
std/mem.zig+10-5| ... | ... | @@ -49,11 +49,16 @@ pub const Allocator = struct { |
| 49 | 49 | } |
| 50 | 50 | |
| 51 | 51 | fn free(self: &Allocator, memory: var) { |
| 52 | const const_slice = ([]const u8)(memory); | |
| 53 | if (memory.len == 0) | |
| 54 | return; | |
| 55 | const ptr = @intToPtr(&u8, @ptrToInt(const_slice.ptr)); | |
| 56 | self.freeFn(self, ptr); | |
| 52 | const ptr = if (@typeId(@typeOf(memory)) == builtin.TypeId.Pointer) { | |
| 53 | memory | |
| 54 | } else { | |
| 55 | const const_slice = ([]const u8)(memory); | |
| 56 | if (memory.len == 0) | |
| 57 | return; | |
| 58 | const_slice.ptr | |
| 59 | }; | |
| 60 | const non_const_ptr = @intToPtr(&u8, @ptrToInt(ptr)); | |
| 61 | self.freeFn(self, non_const_ptr); | |
| 57 | 62 | } |
| 58 | 63 | }; |
| 59 | 64 |
std/os/child_process.zig+189-61| ... | ... | @@ -8,20 +8,31 @@ const assert = debug.assert; |
| 8 | 8 | const BufMap = @import("../buf_map.zig").BufMap; |
| 9 | 9 | const builtin = @import("builtin"); |
| 10 | 10 | const Os = builtin.Os; |
| 11 | const LinkedList = @import("../linked_list.zig").LinkedList; | |
| 11 | 12 | |
| 12 | 13 | error PermissionDenied; |
| 13 | 14 | error ProcessNotFound; |
| 14 | 15 | |
| 16 | var children_nodes = LinkedList(&ChildProcess).init(); | |
| 17 | ||
| 15 | 18 | pub const ChildProcess = struct { |
| 16 | 19 | pid: i32, |
| 20 | ||
| 17 | 21 | err_pipe: [2]i32, |
| 22 | llnode: LinkedList(&ChildProcess).Node, | |
| 23 | allocator: &mem.Allocator, | |
| 24 | ||
| 25 | stdin: ?&io.OutStream, | |
| 26 | stdout: ?&io.InStream, | |
| 27 | stderr: ?&io.InStream, | |
| 18 | 28 | |
| 19 | stdin: ?io.OutStream, | |
| 20 | stdout: ?io.InStream, | |
| 21 | stderr: ?io.InStream, | |
| 29 | term: ?%Term, | |
| 30 | ||
| 31 | /// Possibly called from a signal handler. | |
| 32 | onTerm: ?fn(&ChildProcess), | |
| 22 | 33 | |
| 23 | 34 | pub const Term = enum { |
| 24 | Clean: i32, | |
| 35 | Exited: i32, | |
| 25 | 36 | Signal: i32, |
| 26 | 37 | Stopped: i32, |
| 27 | 38 | Unknown: i32, |
| ... | ... | @@ -34,13 +45,15 @@ pub const ChildProcess = struct { |
| 34 | 45 | Close, |
| 35 | 46 | }; |
| 36 | 47 | |
| 48 | /// onTerm can be called before `spawn` returns. | |
| 37 | 49 | pub fn spawn(exe_path: []const u8, args: []const []const u8, |
| 38 | 50 | cwd: ?[]const u8, env_map: &const BufMap, |
| 39 | stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess | |
| 51 | stdin: StdIo, stdout: StdIo, stderr: StdIo, | |
| 52 | onTerm: ?fn(&ChildProcess), allocator: &Allocator) -> %&ChildProcess | |
| 40 | 53 | { |
| 41 | 54 | switch (builtin.os) { |
| 42 | 55 | Os.linux, Os.macosx, Os.ios, Os.darwin => { |
| 43 | return spawnPosix(exe_path, args, cwd, env_map, stdin, stdout, stderr, allocator); | |
| 56 | return spawnPosix(exe_path, args, cwd, env_map, stdin, stdout, stderr, onTerm, allocator); | |
| 44 | 57 | }, |
| 45 | 58 | else => @compileError("Unsupported OS"), |
| 46 | 59 | } |
| ... | ... | @@ -48,6 +61,12 @@ pub const ChildProcess = struct { |
| 48 | 61 | |
| 49 | 62 | /// Forcibly terminates child process and then cleans up all resources. |
| 50 | 63 | pub fn kill(self: &ChildProcess) -> %Term { |
| 64 | block_SIGCHLD(); | |
| 65 | defer restore_SIGCHLD(); | |
| 66 | ||
| 67 | if (self.term) |term| { | |
| 68 | return term; | |
| 69 | } | |
| 51 | 70 | const ret = posix.kill(self.pid, posix.SIGTERM); |
| 52 | 71 | const err = posix.getErrno(ret); |
| 53 | 72 | if (err > 0) { |
| ... | ... | @@ -58,37 +77,60 @@ pub const ChildProcess = struct { |
| 58 | 77 | else => error.Unexpected, |
| 59 | 78 | }; |
| 60 | 79 | } |
| 61 | return self.wait(); | |
| 80 | self.waitUnwrapped(); | |
| 81 | return ??self.term; | |
| 62 | 82 | } |
| 63 | 83 | |
| 64 | 84 | /// Blocks until child process terminates and then cleans up all resources. |
| 65 | 85 | pub fn wait(self: &ChildProcess) -> %Term { |
| 66 | defer { | |
| 67 | os.posixClose(self.err_pipe[0]); | |
| 68 | os.posixClose(self.err_pipe[1]); | |
| 69 | }; | |
| 86 | block_SIGCHLD(); | |
| 87 | defer restore_SIGCHLD(); | |
| 70 | 88 | |
| 89 | if (self.term) |term| { | |
| 90 | return term; | |
| 91 | } | |
| 92 | ||
| 93 | self.waitUnwrapped(); | |
| 94 | return ??self.term; | |
| 95 | } | |
| 96 | ||
| 97 | fn waitUnwrapped(self: &ChildProcess) { | |
| 71 | 98 | var status: i32 = undefined; |
| 72 | 99 | while (true) { |
| 73 | 100 | const err = posix.getErrno(posix.waitpid(self.pid, &status, 0)); |
| 74 | 101 | if (err > 0) { |
| 75 | 102 | switch (err) { |
| 76 | posix.EINVAL, posix.ECHILD => unreachable, | |
| 77 | 103 | posix.EINTR => continue, |
| 78 | else => { | |
| 79 | if (self.stdin) |*stdin| { stdin.close(); } | |
| 80 | if (self.stdout) |*stdout| { stdout.close(); } | |
| 81 | if (self.stderr) |*stderr| { stderr.close(); } | |
| 82 | return error.Unexpected; | |
| 83 | }, | |
| 104 | else => unreachable, | |
| 84 | 105 | } |
| 85 | 106 | } |
| 86 | break; | |
| 107 | self.cleanupStreams(); | |
| 108 | self.handleWaitResult(status); | |
| 109 | return; | |
| 87 | 110 | } |
| 111 | } | |
| 112 | ||
| 113 | fn handleWaitResult(self: &ChildProcess, status: i32) { | |
| 114 | self.term = self.cleanupAfterWait(status); | |
| 115 | ||
| 116 | if (self.onTerm) |onTerm| { | |
| 117 | onTerm(self); | |
| 118 | } | |
| 119 | } | |
| 88 | 120 | |
| 89 | if (self.stdin) |*stdin| { stdin.close(); } | |
| 90 | if (self.stdout) |*stdout| { stdout.close(); } | |
| 91 | if (self.stderr) |*stderr| { stderr.close(); } | |
| 121 | fn cleanupStreams(self: &ChildProcess) { | |
| 122 | if (self.stdin) |stdin| { stdin.close(); self.allocator.free(stdin); } | |
| 123 | if (self.stdout) |stdout| { stdout.close(); self.allocator.free(stdout); } | |
| 124 | if (self.stderr) |stderr| { stderr.close(); self.allocator.free(stderr); } | |
| 125 | } | |
| 126 | ||
| 127 | fn cleanupAfterWait(self: &ChildProcess, status: i32) -> %Term { | |
| 128 | children_nodes.remove(&self.llnode); | |
| 129 | ||
| 130 | defer { | |
| 131 | os.posixClose(self.err_pipe[0]); | |
| 132 | os.posixClose(self.err_pipe[1]); | |
| 133 | }; | |
| 92 | 134 | |
| 93 | 135 | // Write @maxValue(ErrInt) to the write end of the err_pipe. This is after |
| 94 | 136 | // waitpid, so this write is guaranteed to be after the child |
| ... | ... | @@ -108,7 +150,7 @@ pub const ChildProcess = struct { |
| 108 | 150 | |
| 109 | 151 | fn statusToTerm(status: i32) -> Term { |
| 110 | 152 | return if (posix.WIFEXITED(status)) { |
| 111 | Term.Clean { posix.WEXITSTATUS(status) } | |
| 153 | Term.Exited { posix.WEXITSTATUS(status) } | |
| 112 | 154 | } else if (posix.WIFSIGNALED(status)) { |
| 113 | 155 | Term.Signal { posix.WTERMSIG(status) } |
| 114 | 156 | } else if (posix.WIFSTOPPED(status)) { |
| ... | ... | @@ -120,8 +162,12 @@ pub const ChildProcess = struct { |
| 120 | 162 | |
| 121 | 163 | fn spawnPosix(exe_path: []const u8, args: []const []const u8, |
| 122 | 164 | maybe_cwd: ?[]const u8, env_map: &const BufMap, |
| 123 | stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess | |
| 165 | stdin: StdIo, stdout: StdIo, stderr: StdIo, | |
| 166 | onTerm: ?fn(&ChildProcess), allocator: &Allocator) -> %&ChildProcess | |
| 124 | 167 | { |
| 168 | // TODO atomically set a flag saying that we already did this | |
| 169 | install_SIGCHLD_handler(); | |
| 170 | ||
| 125 | 171 | const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined; |
| 126 | 172 | %defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); }; |
| 127 | 173 | |
| ... | ... | @@ -143,16 +189,39 @@ pub const ChildProcess = struct { |
| 143 | 189 | const err_pipe = %return makePipe(); |
| 144 | 190 | %defer destroyPipe(err_pipe); |
| 145 | 191 | |
| 146 | const pid = posix.fork(); | |
| 147 | const pid_err = posix.getErrno(pid); | |
| 192 | const child = %return allocator.create(ChildProcess); | |
| 193 | %defer allocator.destroy(child); | |
| 194 | ||
| 195 | const stdin_ptr = if (stdin == StdIo.Pipe) { | |
| 196 | %return allocator.create(io.OutStream) | |
| 197 | } else { | |
| 198 | null | |
| 199 | }; | |
| 200 | const stdout_ptr = if (stdout == StdIo.Pipe) { | |
| 201 | %return allocator.create(io.InStream) | |
| 202 | } else { | |
| 203 | null | |
| 204 | }; | |
| 205 | const stderr_ptr = if (stderr == StdIo.Pipe) { | |
| 206 | %return allocator.create(io.InStream) | |
| 207 | } else { | |
| 208 | null | |
| 209 | }; | |
| 210 | ||
| 211 | block_SIGCHLD(); | |
| 212 | const pid_result = posix.fork(); | |
| 213 | const pid_err = posix.getErrno(pid_result); | |
| 148 | 214 | if (pid_err > 0) { |
| 215 | restore_SIGCHLD(); | |
| 149 | 216 | return switch (pid_err) { |
| 150 | 217 | posix.EAGAIN, posix.ENOMEM, posix.ENOSYS => error.SystemResources, |
| 151 | 218 | else => error.Unexpected, |
| 152 | 219 | }; |
| 153 | 220 | } |
| 154 | if (pid == 0) { | |
| 221 | if (pid_result == 0) { | |
| 155 | 222 | // we are the child |
| 223 | restore_SIGCHLD(); | |
| 224 | ||
| 156 | 225 | setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %% |
| 157 | 226 | |err| forkChildErrReport(err_pipe[1], err); |
| 158 | 227 | setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %% |
| ... | ... | @@ -170,45 +239,53 @@ pub const ChildProcess = struct { |
| 170 | 239 | } |
| 171 | 240 | |
| 172 | 241 | // we are the parent |
| 242 | const pid = i32(pid_result); | |
| 243 | if (stdin_ptr) |outstream| { | |
| 244 | *outstream = io.OutStream { | |
| 245 | .fd = stdin_pipe[1], | |
| 246 | .handle = {}, | |
| 247 | .handle_id = {}, | |
| 248 | .buffer = undefined, | |
| 249 | .index = 0, | |
| 250 | }; | |
| 251 | } | |
| 252 | if (stdout_ptr) |instream| { | |
| 253 | *instream = io.InStream { | |
| 254 | .fd = stdout_pipe[0], | |
| 255 | .handle = {}, | |
| 256 | .handle_id = {}, | |
| 257 | }; | |
| 258 | } | |
| 259 | if (stderr_ptr) |instream| { | |
| 260 | *instream = io.InStream { | |
| 261 | .fd = stderr_pipe[0], | |
| 262 | .handle = {}, | |
| 263 | .handle_id = {}, | |
| 264 | }; | |
| 265 | } | |
| 266 | ||
| 267 | *child = ChildProcess { | |
| 268 | .allocator = allocator, | |
| 269 | .pid = pid, | |
| 270 | .err_pipe = err_pipe, | |
| 271 | .llnode = LinkedList(&ChildProcess).Node.init(child), | |
| 272 | .term = null, | |
| 273 | .onTerm = onTerm, | |
| 274 | .stdin = stdin_ptr, | |
| 275 | .stdout = stdout_ptr, | |
| 276 | .stderr = stderr_ptr, | |
| 277 | }; | |
| 278 | ||
| 279 | children_nodes.prepend(&child.llnode); | |
| 280 | ||
| 281 | restore_SIGCHLD(); | |
| 282 | ||
| 173 | 283 | if (stdin == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); } |
| 174 | 284 | if (stdout == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); } |
| 175 | 285 | if (stderr == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); } |
| 176 | 286 | if (any_ignore) { os.posixClose(dev_null_fd); } |
| 177 | 287 | |
| 178 | return ChildProcess { | |
| 179 | .pid = i32(pid), | |
| 180 | .err_pipe = err_pipe, | |
| 181 | ||
| 182 | .stdin = if (stdin == StdIo.Pipe) { | |
| 183 | io.OutStream { | |
| 184 | .fd = stdin_pipe[1], | |
| 185 | .handle = {}, | |
| 186 | .handle_id = {}, | |
| 187 | .buffer = undefined, | |
| 188 | .index = 0, | |
| 189 | } | |
| 190 | } else { | |
| 191 | null | |
| 192 | }, | |
| 193 | .stdout = if (stdout == StdIo.Pipe) { | |
| 194 | io.InStream { | |
| 195 | .fd = stdout_pipe[0], | |
| 196 | .handle = {}, | |
| 197 | .handle_id = {}, | |
| 198 | } | |
| 199 | } else { | |
| 200 | null | |
| 201 | }, | |
| 202 | .stderr = if (stderr == StdIo.Pipe) { | |
| 203 | io.InStream { | |
| 204 | .fd = stderr_pipe[0], | |
| 205 | .handle = {}, | |
| 206 | .handle_id = {}, | |
| 207 | } | |
| 208 | } else { | |
| 209 | null | |
| 210 | }, | |
| 211 | }; | |
| 288 | return child; | |
| 212 | 289 | } |
| 213 | 290 | |
| 214 | 291 | fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void { |
| ... | ... | @@ -258,3 +335,54 @@ fn readIntFd(fd: i32) -> %ErrInt { |
| 258 | 335 | os.posixRead(fd, bytes[0..]) %% return error.SystemResources; |
| 259 | 336 | return mem.readInt(bytes[0..], ErrInt, true); |
| 260 | 337 | } |
| 338 | ||
| 339 | extern fn sigchld_handler(_: i32) { | |
| 340 | while (true) { | |
| 341 | var status: i32 = undefined; | |
| 342 | const pid_result = posix.waitpid(-1, &status, posix.WNOHANG); | |
| 343 | const err = posix.getErrno(pid_result); | |
| 344 | if (err == posix.ECHILD) { | |
| 345 | return; | |
| 346 | } | |
| 347 | handleTerm(i32(pid_result), status); | |
| 348 | } | |
| 349 | } | |
| 350 | ||
| 351 | fn handleTerm(pid: i32, status: i32) { | |
| 352 | var it = children_nodes.first; | |
| 353 | while (it) |node| : (it = node.next) { | |
| 354 | if (node.data.pid == pid) { | |
| 355 | assert(node.data.term == null); | |
| 356 | node.data.handleWaitResult(status); | |
| 357 | return; | |
| 358 | } | |
| 359 | } | |
| 360 | unreachable; | |
| 361 | } | |
| 362 | ||
| 363 | const sigchld_set = { | |
| 364 | var signal_set = posix.empty_sigset; | |
| 365 | posix.sigaddset(&signal_set, posix.SIGCHLD); | |
| 366 | signal_set | |
| 367 | }; | |
| 368 | ||
| 369 | fn block_SIGCHLD() { | |
| 370 | const err = posix.getErrno(posix.sigprocmask(posix.SIG_BLOCK, &sigchld_set, null)); | |
| 371 | assert(err == 0); | |
| 372 | } | |
| 373 | ||
| 374 | fn restore_SIGCHLD() { | |
| 375 | const err = posix.getErrno(posix.sigprocmask(posix.SIG_UNBLOCK, &sigchld_set, null)); | |
| 376 | assert(err == 0); | |
| 377 | } | |
| 378 | ||
| 379 | const sigchld_action = posix.Sigaction { | |
| 380 | .handler = sigchld_handler, | |
| 381 | .mask = posix.empty_sigset, | |
| 382 | .flags = posix.SA_RESTART | posix.SA_NOCLDSTOP, | |
| 383 | }; | |
| 384 | ||
| 385 | fn install_SIGCHLD_handler() { | |
| 386 | const err = posix.getErrno(posix.sigaction(posix.SIGCHLD, &sigchld_action, null)); | |
| 387 | assert(err == 0); | |
| 388 | } |
std/os/linux.zig+83-8| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | const assert = @import("../debug.zig").assert; | |
| 1 | 2 | const builtin = @import("builtin"); |
| 2 | 3 | const arch = switch (builtin.arch) { |
| 3 | 4 | builtin.Arch.x86_64 => @import("linux_x86_64.zig"), |
| ... | ... | @@ -36,6 +37,22 @@ pub const MAP_STACK = 0x20000; |
| 36 | 37 | pub const MAP_HUGETLB = 0x40000; |
| 37 | 38 | pub const MAP_FILE = 0; |
| 38 | 39 | |
| 40 | pub const WNOHANG = 1; | |
| 41 | pub const WUNTRACED = 2; | |
| 42 | pub const WSTOPPED = 2; | |
| 43 | pub const WEXITED = 4; | |
| 44 | pub const WCONTINUED = 8; | |
| 45 | pub const WNOWAIT = 0x1000000; | |
| 46 | ||
| 47 | pub const SA_NOCLDSTOP = 1; | |
| 48 | pub const SA_NOCLDWAIT = 2; | |
| 49 | pub const SA_SIGINFO = 4; | |
| 50 | pub const SA_ONSTACK = 0x08000000; | |
| 51 | pub const SA_RESTART = 0x10000000; | |
| 52 | pub const SA_NODEFER = 0x40000000; | |
| 53 | pub const SA_RESETHAND = 0x80000000; | |
| 54 | pub const SA_RESTORER = 0x04000000; | |
| 55 | ||
| 39 | 56 | pub const SIGHUP = 1; |
| 40 | 57 | pub const SIGINT = 2; |
| 41 | 58 | pub const SIGQUIT = 3; |
| ... | ... | @@ -100,9 +117,9 @@ pub const SEEK_SET = 0; |
| 100 | 117 | pub const SEEK_CUR = 1; |
| 101 | 118 | pub const SEEK_END = 2; |
| 102 | 119 | |
| 103 | const SIG_BLOCK = 0; | |
| 104 | const SIG_UNBLOCK = 1; | |
| 105 | const SIG_SETMASK = 2; | |
| 120 | pub const SIG_BLOCK = 0; | |
| 121 | pub const SIG_UNBLOCK = 1; | |
| 122 | pub const SIG_SETMASK = 2; | |
| 106 | 123 | |
| 107 | 124 | pub const SOCK_STREAM = 1; |
| 108 | 125 | pub const SOCK_DGRAM = 2; |
| ... | ... | @@ -448,7 +465,7 @@ pub fn getrandom(buf: &u8, count: usize, flags: u32) -> usize { |
| 448 | 465 | } |
| 449 | 466 | |
| 450 | 467 | pub fn kill(pid: i32, sig: i32) -> usize { |
| 451 | arch.syscall2(arch.SYS_kill, usize(pid), usize(sig)) | |
| 468 | arch.syscall2(arch.SYS_kill, @bitCast(usize, isize(pid)), usize(sig)) | |
| 452 | 469 | } |
| 453 | 470 | |
| 454 | 471 | pub fn unlink(path: &const u8) -> usize { |
| ... | ... | @@ -456,17 +473,65 @@ pub fn unlink(path: &const u8) -> usize { |
| 456 | 473 | } |
| 457 | 474 | |
| 458 | 475 | pub fn waitpid(pid: i32, status: &i32, options: i32) -> usize { |
| 459 | arch.syscall4(arch.SYS_wait4, usize(pid), @ptrToInt(status), @bitCast(usize, isize(options)), 0) | |
| 476 | arch.syscall4(arch.SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0) | |
| 460 | 477 | } |
| 461 | 478 | |
| 462 | 479 | pub fn nanosleep(req: &const timespec, rem: ?&timespec) -> usize { |
| 463 | 480 | arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)) |
| 464 | 481 | } |
| 465 | 482 | |
| 483 | pub fn sigprocmask(flags: u32, set: &const sigset_t, oldset: ?&sigset_t) -> usize { | |
| 484 | arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8) | |
| 485 | } | |
| 486 | ||
| 487 | pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) -> usize { | |
| 488 | assert(sig >= 1); | |
| 489 | assert(sig != SIGKILL); | |
| 490 | assert(sig != SIGSTOP); | |
| 491 | var ksa = k_sigaction { | |
| 492 | .handler = act.handler, | |
| 493 | .flags = act.flags | SA_RESTORER, | |
| 494 | .mask = undefined, | |
| 495 | .restorer = @ptrCast(extern fn(), arch.restore_rt), | |
| 496 | }; | |
| 497 | var ksa_old: k_sigaction = undefined; | |
| 498 | @memcpy(@ptrCast(&u8, &ksa.mask), @ptrCast(&const u8, &act.mask), 8); | |
| 499 | const result = arch.syscall4(arch.SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); | |
| 500 | const err = getErrno(result); | |
| 501 | if (err != 0) { | |
| 502 | return result; | |
| 503 | } | |
| 504 | if (oact) |old| { | |
| 505 | old.handler = ksa_old.handler; | |
| 506 | old.flags = @truncate(u32, ksa_old.flags); | |
| 507 | @memcpy(@ptrCast(&u8, &old.mask), @ptrCast(&const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); | |
| 508 | } | |
| 509 | return 0; | |
| 510 | } | |
| 511 | ||
| 466 | 512 | const NSIG = 65; |
| 467 | const sigset_t = [128]u8; | |
| 468 | const all_mask = []u8 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; | |
| 469 | const app_mask = []u8 { 0xff, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xff, }; | |
| 513 | const sigset_t = [128 / @sizeOf(usize)]usize; | |
| 514 | const all_mask = []usize{@maxValue(usize)}; | |
| 515 | const app_mask = []usize{0xfffffffc7fffffff}; | |
| 516 | ||
| 517 | const k_sigaction = extern struct { | |
| 518 | handler: extern fn(i32), | |
| 519 | flags: usize, | |
| 520 | restorer: extern fn(), | |
| 521 | mask: [2]u32, | |
| 522 | }; | |
| 523 | ||
| 524 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | |
| 525 | pub const Sigaction = struct { | |
| 526 | handler: extern fn(i32), | |
| 527 | mask: sigset_t, | |
| 528 | flags: u32, | |
| 529 | }; | |
| 530 | ||
| 531 | pub const SIG_ERR = @intToPtr(extern fn(i32), @maxValue(usize)); | |
| 532 | pub const SIG_DFL = @intToPtr(extern fn(i32), 0); | |
| 533 | pub const SIG_IGN = @intToPtr(extern fn(i32), 1); | |
| 534 | pub const empty_sigset = []usize{0} ** sigset_t.len; | |
| 470 | 535 | |
| 471 | 536 | pub fn raise(sig: i32) -> usize { |
| 472 | 537 | var set: sigset_t = undefined; |
| ... | ... | @@ -489,6 +554,16 @@ fn restoreSignals(set: &sigset_t) { |
| 489 | 554 | _ = arch.syscall4(arch.SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG/8); |
| 490 | 555 | } |
| 491 | 556 | |
| 557 | pub fn sigaddset(set: &sigset_t, sig: u6) { | |
| 558 | const s = sig - 1; | |
| 559 | (*set)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); | |
| 560 | } | |
| 561 | ||
| 562 | pub fn sigismember(set: &const sigset_t, sig: u6) -> bool { | |
| 563 | const s = sig - 1; | |
| 564 | return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; | |
| 565 | } | |
| 566 | ||
| 492 | 567 | |
| 493 | 568 | pub const sa_family_t = u16; |
| 494 | 569 | pub const socklen_t = u32; |
std/os/linux_i386.zig+17| ... | ... | @@ -486,6 +486,23 @@ pub inline fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, |
| 486 | 486 | [arg6] "{ebp}" (arg6)) |
| 487 | 487 | } |
| 488 | 488 | |
| 489 | pub nakedcc fn restore() { | |
| 490 | asm volatile ( | |
| 491 | \\popl %%eax | |
| 492 | \\movl $119, %%eax | |
| 493 | \\int $0x80 | |
| 494 | : | |
| 495 | : | |
| 496 | : "rcx", "r11") | |
| 497 | } | |
| 498 | ||
| 499 | pub nakedcc fn restore_rt() { | |
| 500 | asm volatile ("int $0x80" | |
| 501 | : | |
| 502 | : [number] "{eax}" (usize(SYS_rt_sigreturn)) | |
| 503 | : "rcx", "r11") | |
| 504 | } | |
| 505 | ||
| 489 | 506 | export struct msghdr { |
| 490 | 507 | msg_name: &u8, |
| 491 | 508 | msg_namelen: socklen_t, |
std/os/linux_x86_64.zig+8| ... | ... | @@ -442,6 +442,14 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz |
| 442 | 442 | : "rcx", "r11") |
| 443 | 443 | } |
| 444 | 444 | |
| 445 | pub nakedcc fn restore_rt() { | |
| 446 | asm volatile ("syscall" | |
| 447 | : | |
| 448 | : [number] "{rax}" (usize(SYS_rt_sigreturn)) | |
| 449 | : "rcx", "r11") | |
| 450 | } | |
| 451 | ||
| 452 | ||
| 445 | 453 | pub const msghdr = extern struct { |
| 446 | 454 | msg_name: &u8, |
| 447 | 455 | msg_namelen: socklen_t, |
test/cases/array.zig+10| ... | ... | @@ -86,3 +86,13 @@ test "array literal with specified size" { |
| 86 | 86 | assert(array[0] == 1); |
| 87 | 87 | assert(array[1] == 2); |
| 88 | 88 | } |
| 89 | ||
| 90 | test "array child property" { | |
| 91 | var x: [5]i32 = undefined; | |
| 92 | assert(@typeOf(x).child == i32); | |
| 93 | } | |
| 94 | ||
| 95 | test "array len property" { | |
| 96 | var x: [5]i32 = undefined; | |
| 97 | assert(@typeOf(x).len == 5); | |
| 98 | } |
test/tests.zig+8-8| ... | ... | @@ -238,7 +238,7 @@ pub const CompareOutputContext = struct { |
| 238 | 238 | %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name); |
| 239 | 239 | |
| 240 | 240 | var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map, |
| 241 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| | |
| 241 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err| | |
| 242 | 242 | { |
| 243 | 243 | debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); |
| 244 | 244 | }; |
| ... | ... | @@ -253,7 +253,7 @@ pub const CompareOutputContext = struct { |
| 253 | 253 | debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); |
| 254 | 254 | }; |
| 255 | 255 | switch (term) { |
| 256 | Term.Clean => |code| { | |
| 256 | Term.Exited => |code| { | |
| 257 | 257 | if (code != 0) { |
| 258 | 258 | %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code); |
| 259 | 259 | return error.TestFailed; |
| ... | ... | @@ -313,7 +313,7 @@ pub const CompareOutputContext = struct { |
| 313 | 313 | %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name); |
| 314 | 314 | |
| 315 | 315 | var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, null, &b.env_map, |
| 316 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| | |
| 316 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err| | |
| 317 | 317 | { |
| 318 | 318 | debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); |
| 319 | 319 | }; |
| ... | ... | @@ -324,7 +324,7 @@ pub const CompareOutputContext = struct { |
| 324 | 324 | |
| 325 | 325 | const debug_trap_signal: i32 = 5; |
| 326 | 326 | switch (term) { |
| 327 | Term.Clean => |code| { | |
| 327 | Term.Exited => |code| { | |
| 328 | 328 | %%io.stderr.printf("\nProgram expected to hit debug trap (signal {}) " ++ |
| 329 | 329 | "but exited with return code {}\n", debug_trap_signal, code); |
| 330 | 330 | return error.TestFailed; |
| ... | ... | @@ -557,7 +557,7 @@ pub const CompileErrorContext = struct { |
| 557 | 557 | } |
| 558 | 558 | |
| 559 | 559 | var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map, |
| 560 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| | |
| 560 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err| | |
| 561 | 561 | { |
| 562 | 562 | debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err)); |
| 563 | 563 | }; |
| ... | ... | @@ -572,7 +572,7 @@ pub const CompileErrorContext = struct { |
| 572 | 572 | debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err)); |
| 573 | 573 | }; |
| 574 | 574 | switch (term) { |
| 575 | Term.Clean => |code| { | |
| 575 | Term.Exited => |code| { | |
| 576 | 576 | if (code == 0) { |
| 577 | 577 | %%io.stderr.printf("Compilation incorrectly succeeded\n"); |
| 578 | 578 | return error.TestFailed; |
| ... | ... | @@ -819,7 +819,7 @@ pub const ParseCContext = struct { |
| 819 | 819 | } |
| 820 | 820 | |
| 821 | 821 | var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), null, &b.env_map, |
| 822 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| | |
| 822 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, null, b.allocator) %% |err| | |
| 823 | 823 | { |
| 824 | 824 | debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err)); |
| 825 | 825 | }; |
| ... | ... | @@ -834,7 +834,7 @@ pub const ParseCContext = struct { |
| 834 | 834 | debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err)); |
| 835 | 835 | }; |
| 836 | 836 | switch (term) { |
| 837 | Term.Clean => |code| { | |
| 837 | Term.Exited => |code| { | |
| 838 | 838 | if (code != 0) { |
| 839 | 839 | %%io.stderr.printf("Compilation failed with exit code {}\n", code); |
| 840 | 840 | return error.TestFailed; |