Skip to content

Commit

Permalink
Parse messages from guru on close_cb, not exit_cb
Browse files Browse the repository at this point in the history
There is a race condition in `async_guru` in which not all messages from
a `guru` call are displayed:

1. An async `guru` call is started.

2. Lines streamed from `guru` are appended to a message list in
   `callback()`.

3. The `guru` process ends, `exit_cb()` is called, and the list of
   messages is processed for display.

4. The rest of the lines from `guru` are appended to the message list.

Note that `callback` may still be called after `exit_cb`, as noted in
`:help exit_cb`:

	"exit_cb": handler	…
				Note that data can be buffered,
				callbacks may still be called after
				the process ends.

This race was recently exposed by [Vim v8.0.1073][vim], which avoids a
redraw after statusline evaluation changes a highlight. Since vim-go
modifies the goStatusLineColor highlight definition on async calls, the
corresponding redraw acted as a delay, masking this data race.

The solution to this problem is to process `guru` output in `close_cb`,
which is called after all `callback` calls are complete.

[vim]: vim/vim@ba2929b
  • Loading branch information
guns committed Sep 11, 2017
1 parent b71f2b7 commit 17c0385
Showing 1 changed file with 11 additions and 4 deletions.
15 changes: 11 additions & 4 deletions autoload/go/guru.vim
Original file line number Diff line number Diff line change
Expand Up @@ -159,31 +159,38 @@ function! s:async_guru(args) abort
call add(messages, a:msg)
endfunction

function! s:exit_cb(job, exitval) closure
let out = join(messages, "\n")
let status = {}
let exitval = 0

function! s:exit_cb(job, exitval) closure
let status = {
\ 'desc': 'last status',
\ 'type': statusline_type,
\ 'state': "finished",
\ }

if a:exitval
let exitval = a:exitval
let status.state = "failed"
endif

call go#statusline#Update(status_dir, status)
endfunction

function! s:close_cb(ch) closure
let out = join(messages, "\n")

if has_key(a:args, 'custom_parse')
call a:args.custom_parse(a:exitval, out)
call a:args.custom_parse(exitval, out)
else
call s:parse_guru_output(a:exitval, out, a:args.mode)
call s:parse_guru_output(exitval, out, a:args.mode)
endif
endfunction

let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ }

if has_key(result, 'stdin_content')
Expand Down

0 comments on commit 17c0385

Please sign in to comment.