jobwait(): fix race if job exits before waiting on it

Problem:  If a job exits while waiting on another job, the on_exit
          handler is queued but f_jobwait() skips it.
Solution: Always do process_wait(), so that handlers are run during
          f_jobwait().

fix #8302

Test case:
    $ BUSTED_ARGS="--repeat=2000 --no-keep-going" TEST_FILE=test/functional/core/job_spec.lua TEST_FILTER=waiting make functionaltest

Failure example (macOS CI):
    FAILED  test/functional/core/job_spec.lua: jobs jobwait will run callbacks while waiting
    test/functional/core/job_spec.lua:606: Expected objects to be the same.
    Passed in:
    (table: 0x1be77c80) {
      [1] = 'notification'
      [2] = 'wait'
     *[3] = {
       *[1] = 3 } }
    Expected:
    (table: 0x1be77d10) {
      [1] = 'notification'
      [2] = 'wait'
     *[3] = {
       *[1] = 4 } }
    stack traceback:
      test/functional/core/job_spec.lua:606: in function <test/functional/core/job_spec.lua:583
This commit is contained in:
Justin M. Keyes 2019-09-02 18:01:13 +02:00
parent a00eb23c27
commit 58318af718
3 changed files with 7 additions and 7 deletions

View File

@ -5411,7 +5411,8 @@ jobstop({id}) *jobstop()*
See |job-control|.
jobwait({ids}[, {timeout}]) *jobwait()*
Wait for a set of jobs to complete.
Wait for a set of jobs and their |on_exit| handlers to
complete.
{ids} is a list of |job-id|s to wait for.
{timeout} is the maximum waiting time in milliseconds, -1
@ -5427,10 +5428,10 @@ jobwait({ids}[, {timeout}]) *jobwait()*
Returns a list of len({ids}) integers, where each integer is
the wait-result of the corresponding job. Each wait-result is
one of the following:
* Exit-code, if the job exited
* -1 if the timeout was exceeded
* -2 if the job was interrupted
* -3 if the |job-id| is invalid
Exit-code, if the job exited
-1 if the timeout was exceeded
-2 if the job was interrupted (by |CTRL-C|)
-3 if the job-id is invalid
join({list} [, {sep}]) *join()*
Join the items in {list} together into one String.

View File

@ -12487,7 +12487,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// if the job already exited, but wasn't freed yet
if (jobs[i] == NULL || jobs[i]->stream.proc.status >= 0) {
if (jobs[i] == NULL) {
continue;
}

View File

@ -607,7 +607,6 @@ describe('jobs', function()
\ ])
call rpcnotify(g:channel, 'wait', sort(g:jobs), sort(g:exits))
]])
assert:set_parameter('TableFormatLevel', 1000000)
eq({'notification', 'wait',
{{3,4,5,6}, {3,4,5,6}}}, next_msg())
end)