mirror of
https://github.com/neovim/neovim.git
synced 2025-09-28 22:18:33 +00:00
Move signal handling to libuv event loop
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`
This commit is contained in:
105
src/os/event.c
105
src/os/event.c
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
Reference in New Issue
Block a user