aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os/job.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os/job.c')
-rw-r--r--src/nvim/os/job.c146
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)