diff options
Diffstat (limited to 'src/nvim/os/job.c')
-rw-r--r-- | src/nvim/os/job.c | 146 |
1 files changed, 77 insertions, 69 deletions
diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c index f9f94158ae..b369004e47 100644 --- a/src/nvim/os/job.c +++ b/src/nvim/os/job.c @@ -37,6 +37,7 @@ struct job { int pending_closes; // If the job was already stopped bool stopped; + bool defer; // Data associated with the job void *data; // Callbacks @@ -128,14 +129,18 @@ void job_teardown() /// @param stderr_cb Callback that will be invoked when data is available /// on stderr /// @param exit_cb Callback that will be invoked when the job exits -/// @return The job id if the job started successfully. If the the first item / -/// of `argv`(the program) could not be executed, -1 will be returned. -// 0 will be returned if the job table is full. -int job_start(char **argv, - void *data, - rstream_cb stdout_cb, - rstream_cb stderr_cb, - job_exit_cb job_exit_cb) +/// @param defer If the job callbacks invocation should be deferred to vim +/// main loop +/// @param[out] The job id if the job started successfully, 0 if the job table +/// is full, -1 if the program could not be executed. +/// @return The job pointer if the job started successfully, NULL otherwise +Job *job_start(char **argv, + void *data, + rstream_cb stdout_cb, + rstream_cb stderr_cb, + job_exit_cb job_exit_cb, + bool defer, + int *status) { int i; Job *job; @@ -149,12 +154,14 @@ int job_start(char **argv, if (i == MAX_RUNNING_JOBS) { // No free slots - return 0; + *status = 0; + return NULL; } job = xmalloc(sizeof(Job)); // Initialize job->id = i + 1; + *status = job->id; job->pending_refs = 3; job->pending_closes = 4; job->data = data; @@ -175,6 +182,7 @@ int job_start(char **argv, job->proc_stdin.data = NULL; job->proc_stdout.data = NULL; job->proc_stderr.data = NULL; + job->defer = defer; // Initialize the job std{in,out,err} uv_pipe_init(uv_default_loop(), &job->proc_stdin, 0); @@ -192,7 +200,8 @@ int job_start(char **argv, // Spawn the job if (uv_spawn(uv_default_loop(), &job->proc, &job->proc_opts) != 0) { free_job(job); - return -1; + *status = -1; + return NULL; } // Give all handles a reference to the job @@ -204,8 +213,8 @@ int job_start(char **argv, job->in = wstream_new(JOB_WRITE_MAXMEM); wstream_set_stream(job->in, (uv_stream_t *)&job->proc_stdin); // Start the readable streams - job->out = rstream_new(read_cb, JOB_BUFFER_SIZE, job, true); - job->err = rstream_new(read_cb, JOB_BUFFER_SIZE, job, true); + job->out = rstream_new(read_cb, JOB_BUFFER_SIZE, job, defer); + job->err = rstream_new(read_cb, JOB_BUFFER_SIZE, job, defer); rstream_set_stream(job->out, (uv_stream_t *)&job->proc_stdout); rstream_set_stream(job->err, (uv_stream_t *)&job->proc_stderr); rstream_start(job->out); @@ -219,77 +228,64 @@ int job_start(char **argv, } job_count++; - return job->id; + return job; } -/// Terminates a job. This is a non-blocking operation, but if the job exists -/// it's guaranteed to succeed(SIGKILL will eventually be sent) +/// Finds a job instance by id /// /// @param id The job id -/// @return true if the stop request was successfully sent, false if the job -/// id is invalid(probably because it has already stopped) -bool job_stop(int id) +/// @return the Job instance +Job *job_find(int id) { - Job *job = find_job(id); + Job *job; - if (job == NULL || job->stopped) { - return false; + if (id <= 0 || id > MAX_RUNNING_JOBS || !(job = table[id - 1]) + || job->stopped) { + return NULL; } - job->stopped = true; + return job; +} - return true; +/// Terminates a job. This is a non-blocking operation, but if the job exists +/// it's guaranteed to succeed(SIGKILL will eventually be sent) +/// +/// @param job The Job instance +void job_stop(Job *job) +{ + job->stopped = true; } /// Writes data to the job's stdin. This is a non-blocking operation, it /// returns when the write request was sent. /// -/// @param id The job id -/// @param data Buffer containing the data to be written -/// @param len Size of the data -/// @return true if the write request was successfully sent, false if the job -/// id is invalid(probably because it has already stopped) -bool job_write(int id, char *data, uint32_t len) +/// @param job The Job instance +/// @param buffer The buffer which contains the data to be written +/// @return true if the write request was successfully sent, false if writing +/// to the job stream failed (possibly because the OS buffer is full) +bool job_write(Job *job, WBuffer *buffer) { - Job *job = find_job(id); - - if (job == NULL || job->stopped) { - free(data); - return false; - } - - if (!wstream_write(job->in, wstream_new_buffer(data, len, false))) { - job_stop(job->id); - return false; - } + return wstream_write(job->in, buffer); +} - return true; +/// Sets the `defer` flag for a Job instance +/// +/// @param rstream The Job id +/// @param defer The new value for the flag +void job_set_defer(Job *job, bool defer) +{ + job->defer = defer; + rstream_set_defer(job->out, defer); + rstream_set_defer(job->err, defer); } + /// Runs the read callback associated with the job exit event /// /// @param event Object containing data necessary to invoke the callback void job_exit_event(Event event) { - Job *job = event.data.job; - - // Free the slot now, 'exit_cb' may want to start another job to replace - // this one - table[job->id - 1] = NULL; - - if (job->exit_cb) { - // Invoke the exit callback - job->exit_cb(job, job->data); - } - - // Free the job resources - free_job(job); - - // Stop polling job status if this was the last - job_count--; - if (job_count == 0) { - uv_prepare_stop(&job_prepare); - } + job_exit_callback(event.data.job); } /// Get the job id @@ -310,18 +306,30 @@ void *job_data(Job *job) return job->data; } -static bool is_alive(Job *job) +static void job_exit_callback(Job *job) { - return uv_process_kill(&job->proc, 0) == 0; -} + // Free the slot now, 'exit_cb' may want to start another job to replace + // this one + table[job->id - 1] = NULL; -static Job * find_job(int id) -{ - if (id <= 0 || id > MAX_RUNNING_JOBS) { - return NULL; + if (job->exit_cb) { + // Invoke the exit callback + job->exit_cb(job, job->data); + } + + // Free the job resources + free_job(job); + + // Stop polling job status if this was the last + job_count--; + if (job_count == 0) { + uv_prepare_stop(&job_prepare); } +} - return table[id - 1]; +static bool is_alive(Job *job) +{ + return uv_process_kill(&job->proc, 0) == 0; } static void free_job(Job *job) @@ -385,7 +393,7 @@ static void emit_exit_event(Job *job) Event event; event.type = kEventJobExit; event.data.job = job; - event_push(event); + event_push(event, true); } static void close_cb(uv_handle_t *handle) |