|
|
@ -34,6 +34,7 @@ const ProcessWithOutput = struct {
|
|
34
|
34
|
stdout_buf: std.ArrayList(u8),
|
|
35
|
35
|
stderr_buf: std.ArrayList(u8),
|
|
36
|
36
|
|
|
|
37
|
poll_fds: [2]std.os.pollfd,
|
|
37
|
38
|
dead_fds: usize = 0,
|
|
38
|
39
|
max_output_bytes: usize,
|
|
39
|
40
|
|
|
|
@ -47,7 +48,17 @@ const ProcessWithOutput = struct {
|
|
47
|
48
|
child.stderr_behavior = std.ChildProcess.StdIo.Pipe;
|
|
48
|
49
|
try child.spawn();
|
|
49
|
50
|
|
|
50
|
|
return ProcessWithOutput{ .process = child, .stdout_buf = std.ArrayList(u8).init(allocator), .stderr_buf = std.ArrayList(u8).init(allocator), .dead_fds = 0, .max_output_bytes = max_output_bytes };
|
|
|
51
|
return ProcessWithOutput{
|
|
|
52
|
.process = child,
|
|
|
53
|
.stdout_buf = std.ArrayList(u8).init(allocator),
|
|
|
54
|
.stderr_buf = std.ArrayList(u8).init(allocator),
|
|
|
55
|
.poll_fds = [_]std.os.pollfd{
|
|
|
56
|
.{ .fd = child.stdout.?.handle, .events = std.os.POLL.IN, .revents = undefined },
|
|
|
57
|
.{ .fd = child.stderr.?.handle, .events = std.os.POLL.IN, .revents = undefined },
|
|
|
58
|
},
|
|
|
59
|
.dead_fds = 0,
|
|
|
60
|
.max_output_bytes = max_output_bytes,
|
|
|
61
|
};
|
|
51
|
62
|
}
|
|
52
|
63
|
|
|
53
|
64
|
fn is_running(self: *ProcessWithOutput) bool {
|
|
|
@ -73,11 +84,6 @@ const ProcessWithOutput = struct {
|
|
73
|
84
|
return;
|
|
74
|
85
|
}
|
|
75
|
86
|
|
|
76
|
|
var poll_fds = [_]std.os.pollfd{
|
|
77
|
|
.{ .fd = self.process.stdout.?.handle, .events = std.os.POLL.IN, .revents = undefined },
|
|
78
|
|
.{ .fd = self.process.stderr.?.handle, .events = std.os.POLL.IN, .revents = undefined },
|
|
79
|
|
};
|
|
80
|
|
|
|
81
|
87
|
// We ask for ensureTotalCapacity with this much extra space. This has more of an
|
|
82
|
88
|
// effect on small reads because once the reads start to get larger the amount
|
|
83
|
89
|
// of space an ArrayList will allocate grows exponentially.
|
|
|
@ -85,11 +91,11 @@ const ProcessWithOutput = struct {
|
|
85
|
91
|
|
|
86
|
92
|
const err_mask = std.os.POLL.ERR | std.os.POLL.NVAL | std.os.POLL.HUP;
|
|
87
|
93
|
|
|
88
|
|
if (self.dead_fds >= poll_fds.len) {
|
|
|
94
|
if (self.dead_fds >= self.poll_fds.len) {
|
|
89
|
95
|
return;
|
|
90
|
96
|
}
|
|
91
|
97
|
|
|
92
|
|
const events = try std.os.poll(&poll_fds, 0);
|
|
|
98
|
const events = try std.os.poll(&self.poll_fds, 0);
|
|
93
|
99
|
if (events == 0) {
|
|
94
|
100
|
return;
|
|
95
|
101
|
}
|
|
|
@ -100,45 +106,47 @@ const ProcessWithOutput = struct {
|
|
100
|
106
|
// conditions.
|
|
101
|
107
|
// It's still pstd.ossible to read after a POLL.HUP is received, always
|
|
102
|
108
|
// check if there's some data waiting to be read first.
|
|
103
|
|
if (poll_fds[0].revents & std.os.POLL.IN != 0) {
|
|
|
109
|
if (self.poll_fds[0].revents & std.os.POLL.IN != 0) {
|
|
104
|
110
|
// stdout is ready.
|
|
105
|
111
|
const new_capacity = std.math.min(self.stdout_buf.items.len + bump_amt, self.max_output_bytes);
|
|
106
|
112
|
try self.stdout_buf.ensureTotalCapacity(new_capacity);
|
|
107
|
113
|
const buf = self.stdout_buf.unusedCapacitySlice();
|
|
108
|
114
|
if (buf.len == 0) return error.StdoutStreamTooLong;
|
|
109
|
|
const nread = try std.os.read(poll_fds[0].fd, buf);
|
|
|
115
|
const nread = try std.os.read(self.poll_fds[0].fd, buf);
|
|
110
|
116
|
self.stdout_buf.items.len += nread;
|
|
111
|
117
|
|
|
112
|
118
|
std.debug.print("read {d} bytes ({d} total, {d} max)\n", .{ nread, self.stdout_buf.items.len, self.max_output_bytes });
|
|
113
|
119
|
|
|
114
|
120
|
// Remove the fd when the EOF condition is met.
|
|
115
|
|
remove_stdout = nread == 0;
|
|
|
121
|
//remove_stdout = nread == 0;
|
|
116
|
122
|
} else {
|
|
117
|
|
remove_stdout = poll_fds[0].revents & err_mask != 0;
|
|
|
123
|
remove_stdout = (self.poll_fds[0].revents & err_mask) != 0;
|
|
118
|
124
|
}
|
|
119
|
125
|
|
|
120
|
|
if (poll_fds[1].revents & std.os.POLL.IN != 0) {
|
|
|
126
|
if (self.poll_fds[1].revents & std.os.POLL.IN != 0) {
|
|
121
|
127
|
// stderr is ready.
|
|
122
|
128
|
const new_capacity = std.math.min(self.stderr_buf.items.len + bump_amt, self.max_output_bytes);
|
|
123
|
129
|
try self.stderr_buf.ensureTotalCapacity(new_capacity);
|
|
124
|
130
|
const buf = self.stderr_buf.unusedCapacitySlice();
|
|
125
|
131
|
if (buf.len == 0) return error.StderrStreamTooLong;
|
|
126
|
|
const nread = try std.os.read(poll_fds[1].fd, buf);
|
|
|
132
|
const nread = try std.os.read(self.poll_fds[1].fd, buf);
|
|
127
|
133
|
self.stderr_buf.items.len += nread;
|
|
128
|
134
|
|
|
129
|
135
|
// Remove the fd when the EOF condition is met.
|
|
130
|
|
remove_stderr = nread == 0;
|
|
|
136
|
//remove_stderr = nread == 0;
|
|
131
|
137
|
} else {
|
|
132
|
|
remove_stderr = poll_fds[1].revents & err_mask != 0;
|
|
|
138
|
remove_stderr = self.poll_fds[1].revents & err_mask != 0;
|
|
133
|
139
|
}
|
|
134
|
140
|
|
|
135
|
141
|
// Exclude the fds that signaled an error.
|
|
136
|
142
|
if (remove_stdout) {
|
|
137
|
|
poll_fds[0].fd = -1;
|
|
|
143
|
std.debug.print("remove stdout\n", .{});
|
|
|
144
|
self.poll_fds[0].fd = -1;
|
|
138
|
145
|
self.dead_fds += 1;
|
|
139
|
146
|
}
|
|
140
|
147
|
if (remove_stderr) {
|
|
141
|
|
poll_fds[1].fd = -1;
|
|
|
148
|
std.debug.print("remove stderr\n", .{});
|
|
|
149
|
self.poll_fds[1].fd = -1;
|
|
142
|
150
|
self.dead_fds += 1;
|
|
143
|
151
|
}
|
|
144
|
152
|
}
|