Files
neovim/src/os/event.c
Thiago de Arruda 9acb960713 Refactor job control to use RStream events
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
```
2014-04-18 16:11:59 -03:00

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);
}