Skip to content

Commit

Permalink
Move signal handling to libuv event loop
Browse files Browse the repository at this point in the history
This removes all signal handling code from os_unix.c to os/signal.c. Now signal
handling is done like this:

- Watchers for signals are registered with libuv default event loop
- `event_poll` continuously calls `poll_uv_loop` to produce events until it
  receives user input, SIGINT or a timeout
- Any signals received in `poll_uv_loop` will push events to a queue that is
  drained and processed by `event_poll`

Signals aren't handled directly in the libuv callback to avoid recursion in the
event loop(which isn't supported by libuv).

The same principle will apply to other events in the future: Push to a queue
from a libuv callback and drain it from `event_poll`
  • Loading branch information
tarruda committed Apr 1, 2014
1 parent 40879af commit 774f668
Show file tree
Hide file tree
Showing 10 changed files with 295 additions and 608 deletions.
9 changes: 3 additions & 6 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "version.h"
#include "window.h"
#include "os/os.h"
#include "os/signal.h"

/* Maximum number of commands from + or -c arguments. */
#define MAX_ARG_CMDS 10
Expand Down Expand Up @@ -2167,9 +2168,7 @@ mainerr (
char_u *str /* extra argument or NULL */
)
{
#if defined(UNIX) || defined(__EMX__) || defined(VMS)
reset_signals(); /* kill us with CTRL-C here, if you like */
#endif
signal_stop(); /* kill us with CTRL-C here, if you like */

mch_errmsg(longVersion);
mch_errmsg("\n");
Expand Down Expand Up @@ -2214,9 +2213,7 @@ static void usage(void)
N_("-q [errorfile] edit file with first error")
};

#if defined(UNIX) || defined(__EMX__) || defined(VMS)
reset_signals(); /* kill us with CTRL-C here, if you like */
#endif
signal_stop(); /* kill us with CTRL-C here, if you like */

mch_msg(longVersion);
mch_msg(_("\n\nusage:"));
Expand Down
3 changes: 0 additions & 3 deletions src/normal.c
Original file line number Diff line number Diff line change
Expand Up @@ -7480,9 +7480,6 @@ static void nv_open(cmdarg_T *cap)
n_opencmd(cap);
}




/*
* Trigger CursorHold event.
* When waiting for a character for 'updatetime' K_CURSORHOLD is put in the
Expand Down
105 changes: 100 additions & 5 deletions src/os/event.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

#include <uv.h>

#include "os/event.h"
#include "os/input.h"
#include "os/signal.h"
#include "vim.h"
#include "misc2.h"

typedef struct EventNode {
Event *event;
struct EventNode *next;
} EventNode;

static EventNode *head, *tail;
static uv_timer_t timer;
static uv_prepare_t timer_prepare;
static bool poll_uv_loop(int ms);
static void process_all_events(void);
static bool has_pending_events(void);
static void timer_cb(uv_timer_t *handle, int);
static void timer_prepare_cb(uv_prepare_t *, int);

Expand All @@ -17,13 +30,93 @@ void event_init()
input_init();
// Timer to wake the event loop if a timeout argument is passed to
// `event_poll`
// Signals
signal_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)
{
int64_t remaining = ms;
uint64_t end;
bool result;

if (ms > 0) {
// Calculate end time in nanoseconds
end = uv_hrtime() + ms * 1e6;
}

for (;;) {
result = poll_uv_loop((int32_t)remaining);
// Process queued events
process_all_events();

if (ms > 0) {
// Calculate remaining time in milliseconds
remaining = (end - uv_hrtime()) / 1e6;
}

if (input_ready() || got_int) {
// Bail out if we have pending input
return true;
}

if (!result || (ms >= 0 && remaining <= 0)) {
// Or if we timed out
return false;
}
}
}

// Push an event to the queue
void event_push(Event *event)
{
EventNode *node = (EventNode *)xmalloc(sizeof(EventNode));
node->event = event;
node->next = NULL;

if (head == NULL) {
head = node;
} else {
tail->next = node;
}

tail = node;
}

// Runs the appropriate action for each queued event
static void process_all_events()
{
EventNode *next;
Event *event;

while (has_pending_events()) {
next = head->next;
event = head->event;
free(head);
head = next;

switch (event->type) {
case kEventSignal:
signal_handle(event);
break;
default:
abort();
}

}
}

// Checks if there are queued events
bool has_pending_events()
{
return head != NULL;
}

// Wait for some event
static bool poll_uv_loop(int32_t ms)
{
bool timed_out;
uv_run_mode run_mode = UV_RUN_ONCE;
Expand Down Expand Up @@ -55,9 +148,11 @@ bool event_poll(int32_t ms)
uv_run(uv_default_loop(), run_mode);
} while (
// Continue running if ...
!input_ready() // ... we have no input
&& run_mode != UV_RUN_NOWAIT // ... ms != 0
&& !timed_out); // ... we didn't get a timeout
!input_ready() && // we have no input
!has_pending_events() && // no events are waiting to be processed
run_mode != UV_RUN_NOWAIT && // ms != 0
!timed_out // we didn't get a timeout
);

input_stop();

Expand All @@ -66,7 +161,7 @@ bool event_poll(int32_t ms)
uv_timer_stop(&timer);
}

return input_ready();
return input_ready() || has_pending_events();
}

// Set a flag in the `event_poll` loop for signaling of a timeout
Expand Down
10 changes: 10 additions & 0 deletions src/os/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,18 @@
#include <stdint.h>
#include <stdbool.h>

typedef enum {
kEventSignal
} EventType;

typedef struct {
EventType type;
void *data;
} Event;

void event_init(void);
bool event_poll(int32_t ms);
void event_push(Event *event);

#endif // NEOVIM_OS_EVENT_H

10 changes: 6 additions & 4 deletions src/os/input.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

Expand Down Expand Up @@ -58,7 +59,7 @@ void input_init()
}
}

// Check if there's a pending input event
// Check if there's pending input
bool input_ready()
{
return rbuffer.rpos < rbuffer.wpos || eof;
Expand Down Expand Up @@ -126,11 +127,11 @@ int os_inchar(char_u *buf, int maxlen, int32_t ms, int tb_change_cnt)
InbufPollResult result;

if (ms >= 0) {
if ((result = inbuf_poll(ms)) != kInputAvail) {
if ((result = inbuf_poll(ms)) == kInputNone) {
return 0;
}
} else {
if ((result = inbuf_poll(p_ut)) != kInputAvail) {
if ((result = inbuf_poll(p_ut)) == kInputNone) {
if (trigger_cursorhold() && maxlen >= 3
&& !typebuf_changed(tb_change_cnt)) {
buf[0] = K_SPECIAL;
Expand Down Expand Up @@ -177,9 +178,10 @@ static InbufPollResult inbuf_poll(int32_t ms)
return kInputAvail;

if (event_poll(ms)) {
if (rbuffer.rpos == rbuffer.wpos && eof) {
if (!got_int && rbuffer.rpos == rbuffer.wpos && eof) {
return kInputEof;
}

return kInputAvail;
}

Expand Down
Loading

0 comments on commit 774f668

Please sign in to comment.