Przeglądaj źródła

Fix truncated command output

The issue was that poll_fds contained state: When one of the fds was
closed then it would be set to -1, which usually would make `poll()`
ignore it.  But because we recreated poll_fds with every call to
`poll()` we would not save that state, then try to remove it again and
increment `dead_fds` twice for one fd which meant it would not check the
other (still possibly open) fd.

🎉
Luna Stadler 4 lat temu
rodzic
commit
252c08e2e4
1 zmienionych plików z 26 dodań i 18 usunięć
  1. 26 18
      zig/sdl/hello_sdl.zig

+ 26 - 18
zig/sdl/hello_sdl.zig

34
    stdout_buf: std.ArrayList(u8),
34
    stdout_buf: std.ArrayList(u8),
35
    stderr_buf: std.ArrayList(u8),
35
    stderr_buf: std.ArrayList(u8),
36
36
37
    poll_fds: [2]std.os.pollfd,
37
    dead_fds: usize = 0,
38
    dead_fds: usize = 0,
38
    max_output_bytes: usize,
39
    max_output_bytes: usize,
39
40
47
        child.stderr_behavior = std.ChildProcess.StdIo.Pipe;
48
        child.stderr_behavior = std.ChildProcess.StdIo.Pipe;
48
        try child.spawn();
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
    fn is_running(self: *ProcessWithOutput) bool {
64
    fn is_running(self: *ProcessWithOutput) bool {
73
            return;
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
        // We ask for ensureTotalCapacity with this much extra space. This has more of an
87
        // We ask for ensureTotalCapacity with this much extra space. This has more of an
82
        // effect on small reads because once the reads start to get larger the amount
88
        // effect on small reads because once the reads start to get larger the amount
83
        // of space an ArrayList will allocate grows exponentially.
89
        // of space an ArrayList will allocate grows exponentially.
85
91
86
        const err_mask = std.os.POLL.ERR | std.os.POLL.NVAL | std.os.POLL.HUP;
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
            return;
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
        if (events == 0) {
99
        if (events == 0) {
94
            return;
100
            return;
95
        }
101
        }
100
        // conditions.
106
        // conditions.
101
        // It's still pstd.ossible to read after a POLL.HUP is received, always
107
        // It's still pstd.ossible to read after a POLL.HUP is received, always
102
        // check if there's some data waiting to be read first.
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
            // stdout is ready.
110
            // stdout is ready.
105
            const new_capacity = std.math.min(self.stdout_buf.items.len + bump_amt, self.max_output_bytes);
111
            const new_capacity = std.math.min(self.stdout_buf.items.len + bump_amt, self.max_output_bytes);
106
            try self.stdout_buf.ensureTotalCapacity(new_capacity);
112
            try self.stdout_buf.ensureTotalCapacity(new_capacity);
107
            const buf = self.stdout_buf.unusedCapacitySlice();
113
            const buf = self.stdout_buf.unusedCapacitySlice();
108
            if (buf.len == 0) return error.StdoutStreamTooLong;
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
            self.stdout_buf.items.len += nread;
116
            self.stdout_buf.items.len += nread;
111
117
112
            std.debug.print("read {d} bytes ({d} total, {d} max)\n", .{ nread, self.stdout_buf.items.len, self.max_output_bytes });
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
            // Remove the fd when the EOF condition is met.
120
            // Remove the fd when the EOF condition is met.
115
            remove_stdout = nread == 0;
121
            //remove_stdout = nread == 0;
116
        } else {
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
            // stderr is ready.
127
            // stderr is ready.
122
            const new_capacity = std.math.min(self.stderr_buf.items.len + bump_amt, self.max_output_bytes);
128
            const new_capacity = std.math.min(self.stderr_buf.items.len + bump_amt, self.max_output_bytes);
123
            try self.stderr_buf.ensureTotalCapacity(new_capacity);
129
            try self.stderr_buf.ensureTotalCapacity(new_capacity);
124
            const buf = self.stderr_buf.unusedCapacitySlice();
130
            const buf = self.stderr_buf.unusedCapacitySlice();
125
            if (buf.len == 0) return error.StderrStreamTooLong;
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
            self.stderr_buf.items.len += nread;
133
            self.stderr_buf.items.len += nread;
128
134
129
            // Remove the fd when the EOF condition is met.
135
            // Remove the fd when the EOF condition is met.
130
            remove_stderr = nread == 0;
136
            //remove_stderr = nread == 0;
131
        } else {
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
        // Exclude the fds that signaled an error.
141
        // Exclude the fds that signaled an error.
136
        if (remove_stdout) {
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
            self.dead_fds += 1;
145
            self.dead_fds += 1;
139
        }
146
        }
140
        if (remove_stderr) {
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
            self.dead_fds += 1;
150
            self.dead_fds += 1;
143
        }
151
        }
144
    }
152
    }