mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Instead of a single 'job read' callback, job control consumers need to provide
callbacks for "stdout read", "stderr read" and "exit". For vimscript, the
JobActivity autocommand is still used to handle every job event, for example:
```vim
:let srv1_id = jobstart('netcat-server-1', 'nc', ['-l', '9991'])
:let srv2_id = jobstart('netcat-server-2', 'nc', ['-l', '9991'])
function JobEvent()
" v:job_data[0] = the job id
" v:job_data[1] = the event type, one of "stdout", "stderr" or "exit"
" v:job_data[2] = data read from stdout or stderr
if v:job_data[1] == 'stdout'
let str = 'Message from job '.v:job_data[0].': '.v:job_data[2]
elseif v:job_data[1] == 'stderr'
let str = 'Error message from job '.v:job_data[0].': '.v:job_data[2]
else
" Exit
let str = 'Job '.v:job_data[0].' exited'
endif
call append(line('$'), str)
endfunction
au JobActivity netcat-server-* call JobEvent()
```
And to see messages from 'job 1', run in another terminal:
```sh
bash -c "while true; do echo 123; sleep 1; done" | nc localhost 9991
```
136 lines
3.1 KiB
C
136 lines
3.1 KiB
C
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <uv.h>
|
|
|
|
#include "lib/klist.h"
|
|
#include "os/event.h"
|
|
#include "os/input.h"
|
|
#include "os/signal.h"
|
|
#include "os/rstream.h"
|
|
#include "os/job.h"
|
|
#include "vim.h"
|
|
#include "memory.h"
|
|
#include "misc2.h"
|
|
|
|
// event will be cleaned up after it gets processed
|
|
#define _destroy_event(x) // do nothing
|
|
KLIST_INIT(Event, Event, _destroy_event)
|
|
|
|
static klist_t(Event) *event_queue;
|
|
static uv_timer_t timer;
|
|
static uv_prepare_t timer_prepare;
|
|
static void timer_cb(uv_timer_t *handle);
|
|
static void timer_prepare_cb(uv_prepare_t *);
|
|
|
|
void event_init()
|
|
{
|
|
// Initialize the event queue
|
|
event_queue = kl_init(Event);
|
|
// Initialize input events
|
|
input_init();
|
|
// Timer to wake the event loop if a timeout argument is passed to
|
|
// `event_poll`
|
|
// Signals
|
|
signal_init();
|
|
// Jobs
|
|
job_init();
|
|
uv_timer_init(uv_default_loop(), &timer);
|
|
// This prepare handle that actually starts the timer
|
|
uv_prepare_init(uv_default_loop(), &timer_prepare);
|
|
}
|
|
|
|
// Wait for some event
|
|
bool event_poll(int32_t ms)
|
|
{
|
|
bool timed_out;
|
|
uv_run_mode run_mode = UV_RUN_ONCE;
|
|
|
|
if (input_ready()) {
|
|
// If there's a pending input event to be consumed, do it now
|
|
return true;
|
|
}
|
|
|
|
input_start();
|
|
timed_out = false;
|
|
|
|
if (ms > 0) {
|
|
// Timeout passed as argument to the timer
|
|
timer.data = &timed_out;
|
|
// We only start the timer after the loop is running, for that we
|
|
// use an prepare handle(pass the interval as data to it)
|
|
timer_prepare.data = &ms;
|
|
uv_prepare_start(&timer_prepare, timer_prepare_cb);
|
|
} else if (ms == 0) {
|
|
// For ms == 0, we need to do a non-blocking event poll by
|
|
// setting the run mode to UV_RUN_NOWAIT.
|
|
run_mode = UV_RUN_NOWAIT;
|
|
}
|
|
|
|
do {
|
|
// Run one event loop iteration, blocking for events if run_mode is
|
|
// UV_RUN_ONCE
|
|
uv_run(uv_default_loop(), run_mode);
|
|
} while (
|
|
// Continue running if ...
|
|
!input_ready() && // we have no input
|
|
kl_empty(event_queue) && // no events are waiting to be processed
|
|
run_mode != UV_RUN_NOWAIT && // ms != 0
|
|
!timed_out); // we didn't get a timeout
|
|
|
|
input_stop();
|
|
|
|
if (ms > 0) {
|
|
// Stop the timer
|
|
uv_timer_stop(&timer);
|
|
}
|
|
|
|
return input_ready() || event_is_pending();
|
|
}
|
|
|
|
bool event_is_pending()
|
|
{
|
|
return !kl_empty(event_queue);
|
|
}
|
|
|
|
// Push an event to the queue
|
|
void event_push(Event event)
|
|
{
|
|
*kl_pushp(Event, event_queue) = event;
|
|
}
|
|
|
|
// Runs the appropriate action for each queued event
|
|
void event_process()
|
|
{
|
|
Event event;
|
|
|
|
while (kl_shift(Event, event_queue, &event) == 0) {
|
|
switch (event.type) {
|
|
case kEventSignal:
|
|
signal_handle(event);
|
|
break;
|
|
case kEventRStreamData:
|
|
rstream_read_event(event);
|
|
break;
|
|
case kEventJobExit:
|
|
job_exit_event(event);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set a flag in the `event_poll` loop for signaling of a timeout
|
|
static void timer_cb(uv_timer_t *handle)
|
|
{
|
|
*((bool *)handle->data) = true;
|
|
}
|
|
|
|
static void timer_prepare_cb(uv_prepare_t *handle)
|
|
{
|
|
uv_timer_start(&timer, timer_cb, *(uint32_t *)timer_prepare.data, 0);
|
|
uv_prepare_stop(&timer_prepare);
|
|
}
|