Merge #7813 'channels: delay free'

fix #7699
This commit is contained in:
Justin M. Keyes 2018-04-13 00:35:50 +02:00 committed by GitHub
commit 5e18550ddd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 15 deletions

View File

@ -237,15 +237,16 @@ void channel_create_event(Channel *chan, const char *ext_source)
#endif #endif
} }
void channel_incref(Channel *channel) void channel_incref(Channel *chan)
{ {
channel->refcount++; chan->refcount++;
} }
void channel_decref(Channel *channel) void channel_decref(Channel *chan)
{ {
if (!(--channel->refcount)) { if (!(--chan->refcount)) {
multiqueue_put(main_loop.fast_events, free_channel_event, 1, channel); // delay free, so that libuv is done with the handles
multiqueue_put(main_loop.events, free_channel_event, 1, chan);
} }
} }
@ -267,18 +268,18 @@ void callback_reader_start(CallbackReader *reader)
static void free_channel_event(void **argv) static void free_channel_event(void **argv)
{ {
Channel *channel = argv[0]; Channel *chan = argv[0];
if (channel->is_rpc) { if (chan->is_rpc) {
rpc_free(channel); rpc_free(chan);
} }
callback_reader_free(&channel->on_stdout); callback_reader_free(&chan->on_stdout);
callback_reader_free(&channel->on_stderr); callback_reader_free(&chan->on_stderr);
callback_free(&channel->on_exit); callback_free(&chan->on_exit);
pmap_del(uint64_t)(channels, channel->id); pmap_del(uint64_t)(channels, chan->id);
multiqueue_free(channel->events); multiqueue_free(chan->events);
xfree(channel); xfree(chan);
} }
static void channel_destroy_early(Channel *chan) static void channel_destroy_early(Channel *chan)
@ -286,12 +287,15 @@ static void channel_destroy_early(Channel *chan)
if ((chan->id != --next_chan_id)) { if ((chan->id != --next_chan_id)) {
abort(); abort();
} }
pmap_del(uint64_t)(channels, chan->id);
chan->id = 0;
if ((--chan->refcount != 0)) { if ((--chan->refcount != 0)) {
abort(); abort();
} }
free_channel_event((void **)&chan); // uv will keep a reference to handles until next loop tick, so delay free
multiqueue_put(main_loop.events, free_channel_event, 1, chan);
} }

View File

@ -640,6 +640,21 @@ describe('jobs', function()
ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil) ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil)
end) end)
it('does not crash when repeatedly failing to start shell', function()
source([[
set shell=nosuchshell
func! DoIt()
call jobstart('true')
call jobstart('true')
endfunc
]])
-- The crash only triggered if both jobs are cleaned up on the same event
-- loop tick. This is also prevented by try-block, so feed must be used.
feed_command("call DoIt()")
feed('<cr>') -- press RETURN
eq(2,eval('1+1'))
end)
it('jobstop() kills entire process tree #6530', function() it('jobstop() kills entire process tree #6530', function()
command('set shell& shellcmdflag& shellquote& shellpipe& shellredir& shellxquote&') command('set shell& shellcmdflag& shellquote& shellpipe& shellredir& shellxquote&')