fix(channel): handle writing to file instead of pipe (#30520)

This commit is contained in:
zeertzjq
2024-09-26 16:43:51 +08:00
committed by GitHub
parent 3a23149cfc
commit e537379641
4 changed files with 56 additions and 7 deletions

View File

@@ -84,16 +84,16 @@ struct stream {
uv_tty_t tty;
#endif
} uv;
uv_stream_t *uvstream;
uv_stream_t *uvstream; ///< NULL when the stream is a file
uv_buf_t uvbuf;
RBuffer *buffer;
uv_file fd;
uv_file fd; ///< When the stream is a file, this is its file descriptor
stream_read_cb read_cb;
stream_write_cb write_cb;
void *cb_data;
stream_close_cb close_cb, internal_close_cb;
void *close_cb_data, *internal_data;
size_t fpos;
size_t fpos; ///< When the stream is a file, this is the position in file
size_t curmem;
size_t maxmem;
size_t pending_reqs;

View File

@@ -45,6 +45,8 @@ int stream_set_blocking(int fd, bool blocking)
void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream)
FUNC_ATTR_NONNULL_ARG(2)
{
// The underlying stream is either a file or an existing uv stream.
assert(uvstream == NULL ? fd >= 0 : fd < 0);
stream->uvstream = uvstream;
if (fd >= 0) {

View File

@@ -73,6 +73,26 @@ bool wstream_write(Stream *stream, WBuffer *buffer)
// This should not be called after a stream was freed
assert(!stream->closed);
uv_buf_t uvbuf;
uvbuf.base = buffer->data;
uvbuf.len = UV_BUF_LEN(buffer->size);
if (!stream->uvstream) {
uv_fs_t req;
// Synchronous write
uv_fs_write(stream->uv.idle.loop, &req, stream->fd, &uvbuf, 1, (int64_t)stream->fpos, NULL);
uv_fs_req_cleanup(&req);
wstream_release_wbuffer(buffer);
assert(stream->write_cb == NULL);
stream->fpos += (size_t)MAX(req.result, 0);
return req.result > 0;
}
if (stream->curmem > stream->maxmem) {
goto err;
}
@@ -84,10 +104,6 @@ bool wstream_write(Stream *stream, WBuffer *buffer)
data->buffer = buffer;
data->uv_req.data = data;
uv_buf_t uvbuf;
uvbuf.base = buffer->data;
uvbuf.len = UV_BUF_LEN(buffer->size);
if (uv_write(&data->uv_req, stream->uvstream, &uvbuf, 1, write_cb)) {
xfree(data);
goto err;

View File

@@ -288,6 +288,37 @@ describe('channels', function()
eq({ 'notification', 'exit', { 3, 0 } }, next_msg())
end)
it('stdio channel works with stdout redirected to file #30509', function()
t.write_file(
'Xstdio_write.vim',
[[
let chan = stdioopen({})
call chansend(chan, 'foo')
call chansend(chan, 'bar')
qall!
]]
)
local fd = assert(vim.uv.fs_open('Xstdio_redir', 'w', 420))
local exit_code, exit_signal
local handle = vim.uv.spawn(nvim_prog, {
args = { '-u', 'NONE', '-i', 'NONE', '--headless', '-S', 'Xstdio_write.vim' },
-- Simulate shell redirection: "nvim ... > Xstdio_redir". #30509
stdio = { nil, fd, nil },
}, function(code, signal)
vim.uv.stop()
exit_code, exit_signal = code, signal
end)
finally(function()
handle:close()
vim.uv.fs_close(fd)
os.remove('Xstdio_write.vim')
os.remove('Xstdio_redir')
end)
vim.uv.run('default')
eq({ 0, 0 }, { exit_code, exit_signal })
eq('foobar', t.read_file('Xstdio_redir'))
end)
it('can use buffered output mode', function()
skip(fn.executable('grep') == 0, 'missing "grep" command')
source([[