diff options
author | Nicolas Hillegeer <nicolas@hillegeer.com> | 2014-07-21 22:48:55 +0200 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-07-27 14:00:44 -0300 |
commit | 9f624f0937ded2cbb94a4f4f3fc6721d1a9a9cfc (patch) | |
tree | 5ef4f94c1e22ac8f39046b1975fce95ea7322c2d | |
parent | 6af15f706ad330ca0cd4bcadb76b064372b60d87 (diff) | |
download | rneovim-9f624f0937ded2cbb94a4f4f3fc6721d1a9a9cfc.tar.gz rneovim-9f624f0937ded2cbb94a4f4f3fc6721d1a9a9cfc.tar.bz2 rneovim-9f624f0937ded2cbb94a4f4f3fc6721d1a9a9cfc.zip |
job: implement job_wait()
Used to wait synchronously for a job to end.
-rw-r--r-- | src/nvim/os/job.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c index 5c45c3727e..8ad1a7070d 100644 --- a/src/nvim/os/job.c +++ b/src/nvim/os/job.c @@ -14,6 +14,7 @@ #include "nvim/os/event_defs.h" #include "nvim/os/time.h" #include "nvim/os/shell.h" +#include "nvim/os/signal.h" #include "nvim/vim.h" #include "nvim/memory.h" #include "nvim/term.h" @@ -25,6 +26,8 @@ struct job { // Job id the index in the job table plus one. int id; + // Exit status code of the job process + int64_t status; // Number of polls after a SIGTERM that will trigger a SIGKILL int exit_timeout; // exit_cb may be called while there's still pending data from stdout/stderr. @@ -163,6 +166,7 @@ Job *job_start(char **argv, // Initialize job->id = i + 1; *status = job->id; + job->status = -1; job->pending_refs = 3; job->pending_closes = 4; job->data = data; @@ -257,6 +261,64 @@ void job_stop(Job *job) job->stopped = true; } +/// job_wait - synchronously wait for a job to finish +/// +/// @param job The job instance +/// @param ms Number of milliseconds to wait, 0 for not waiting, -1 for +/// waiting until the job quits. +/// @return returns the status code of the exited job. -1 if the job is +/// still running and the `timeout` has expired. Note that this is +/// indistinguishable from the process returning -1 by itself. Which +/// is possible on some OS. +int job_wait(Job *job, int ms) FUNC_ATTR_NONNULL_ALL +{ + // switch to cooked so `got_int` will be set if the user interrupts + int old_mode = cur_tmode; + settmode(TMODE_COOK); + + EventSource sources[] = {job_event_source(job), signal_event_source(), NULL}; + + // keep track of the elapsed time if ms > 0 + uint64_t before = (ms > 0) ? os_hrtime() : 0; + + while (1) { + // check if the job has exited (and the status is available). + if (job->pending_refs == 0) { + break; + } + + event_poll(ms, sources); + + // we'll assume that a user frantically hitting interrupt doesn't like + // the current job. Signal that it has to be killed. + if (got_int) { + job_stop(job); + } + + if (ms == 0) { + break; + } + + // check if the poll timed out, if not, decrease the ms to wait for the + // next run + if (ms > 0) { + uint64_t now = os_hrtime(); + ms -= (int) ((now - before) / 1000000); + before = now; + + // if the time elapsed is greater than the `ms` wait time, break + if (ms <= 0) { + break; + } + } + } + + settmode(old_mode); + + // return -1 for a timeout, the job status otherwise + return (job->pending_refs) ? -1 : (int) job->status; +} + /// Writes data to the job's stdin. This is a non-blocking operation, it /// returns when the write request was sent. /// @@ -377,6 +439,7 @@ static void exit_cb(uv_process_t *proc, int64_t status, int term_signal) { Job *job = handle_get_job((uv_handle_t *)proc); + job->status = status; if (--job->pending_refs == 0) { emit_exit_event(job); } |