fix(channel): fix channel consistency

- Fix the problem that chanclose() does not work for channel created by
  nvim_open_term().
- Fix the problem that the loopback channel is not released.
- Fix the error message when sending raw data to the loopback channel.
This commit is contained in:
erw7 2021-11-12 00:07:03 +09:00 committed by zeertzjq
parent ab456bc304
commit 5051510ade
6 changed files with 52 additions and 5 deletions

View File

@ -1140,6 +1140,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
TerminalOptions topts; TerminalOptions topts;
Channel *chan = channel_alloc(kChannelStreamInternal); Channel *chan = channel_alloc(kChannelStreamInternal);
chan->stream.internal.cb = cb; chan->stream.internal.cb = cb;
chan->stream.internal.closed = false;
topts.data = chan; topts.data = chan;
// NB: overridden in terminal_check_size if a window is already // NB: overridden in terminal_check_size if a window is already
// displaying the buffer // displaying the buffer

View File

@ -138,8 +138,14 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error)
*error = (const char *)e_invstream; *error = (const char *)e_invstream;
return false; return false;
} }
if (chan->term) {
api_free_luaref(chan->stream.internal.cb); api_free_luaref(chan->stream.internal.cb);
chan->stream.internal.cb = LUA_NOREF; chan->stream.internal.cb = LUA_NOREF;
chan->stream.internal.closed = true;
terminal_close(chan->term, 0);
} else {
channel_decref(chan);
}
break; break;
default: default:
@ -536,7 +542,11 @@ size_t channel_send(uint64_t id, char *data, size_t len, bool data_owned, const
} }
if (chan->streamtype == kChannelStreamInternal) { if (chan->streamtype == kChannelStreamInternal) {
if (!chan->term) { if (chan->is_rpc) {
*error = _("Can't send raw data to rpc channel");
goto retfree;
}
if (!chan->term || chan->stream.internal.closed) {
*error = _("Can't send data to closed stream"); *error = _("Can't send data to closed stream");
goto retfree; goto retfree;
} }

View File

@ -44,6 +44,7 @@ typedef struct {
typedef struct { typedef struct {
LuaRef cb; LuaRef cb;
bool closed;
} InternalState; } InternalState;
typedef struct { typedef struct {

View File

@ -317,10 +317,14 @@ void terminal_close(Terminal *term, int status)
term->opts.close_cb(term->opts.data); term->opts.close_cb(term->opts.data);
} }
} else if (!only_destroy) { } else if (!only_destroy) {
// This was called by channel_process_exit_cb() not in process_teardown(). // Associated channel has been closed and the editor is not exiting.
// Do not call the close callback now. Wait for the user to press a key. // Do not call the close callback now. Wait for the user to press a key.
char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN];
if (((Channel *)term->opts.data)->streamtype == kChannelStreamInternal) {
snprintf(msg, sizeof msg, "\r\n[Terminal closed]");
} else {
snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status); snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status);
}
terminal_receive(term, msg, strlen(msg)); terminal_receive(term, msg, strlen(msg));
} }

View File

@ -10,6 +10,8 @@ local nvim_prog = helpers.nvim_prog
local is_os = helpers.is_os local is_os = helpers.is_os
local retry = helpers.retry local retry = helpers.retry
local expect_twostreams = helpers.expect_twostreams local expect_twostreams = helpers.expect_twostreams
local assert_alive = helpers.assert_alive
local pcall_err = helpers.pcall_err
describe('channels', function() describe('channels', function()
local init = [[ local init = [[
@ -314,3 +316,22 @@ describe('channels', function()
eq({"notification", "exit", {id, 1, {''}}}, next_msg()) eq({"notification", "exit", {id, 1, {''}}}, next_msg())
end) end)
end) end)
describe('loopback', function()
before_each(function()
clear()
command("let chan = sockconnect('pipe', v:servername, {'rpc': v:true})")
end)
it('does not crash when sending raw data', function()
eq("Vim(call):Can't send raw data to rpc channel",
pcall_err(command, "call chansend(chan, 'test')"))
assert_alive()
end)
it('are released when closed', function()
local chans = eval('len(nvim_list_chans())')
command('call chanclose(chan)')
eq(chans - 1, eval('len(nvim_list_chans())'))
end)
end)

View File

@ -43,3 +43,13 @@ describe('associated channel is closed and later freed for terminal', function()
eq("Vim(call):E900: Invalid channel id", pcall_err(command, [[call chansend(id, 'test')]])) eq("Vim(call):E900: Invalid channel id", pcall_err(command, [[call chansend(id, 'test')]]))
end) end)
end) end)
describe('channel created by nvim_open_term', function()
before_each(clear)
it('can close', function()
command('let id = nvim_open_term(0, {})')
eq("Vim(call):Can't send data to closed stream",
pcall_err(command, [[call chanclose(id) | call chansend(id, 'test')]]))
end)
end)