diff options
author | Thiago de Arruda <tpadilha84@gmail.com> | 2015-07-16 23:10:04 -0300 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2015-07-17 00:19:19 -0300 |
commit | 991d3ec1e679bb6407f2a5820910d2968424183c (patch) | |
tree | 38810fb657e1e1ea9d77b7a7963e874e8bda99d1 /src/nvim/event | |
parent | 9e42ef4e1312fb6888d2691e9d979b95dd43ec94 (diff) | |
download | rneovim-991d3ec1e679bb6407f2a5820910d2968424183c.tar.gz rneovim-991d3ec1e679bb6407f2a5820910d2968424183c.tar.bz2 rneovim-991d3ec1e679bb6407f2a5820910d2968424183c.zip |
event loop: New abstraction layer with refactored time/signal API
- Add event loop abstraction module under src/nvim/event. The
src/nvim/event/loop module replaces src/nvim/os/event
- Remove direct dependency on libuv signal/timer API and use the new abstraction
instead.
- Replace all references to uv_default_loop() by &loop.uv, a new global variable
that wraps libuv main event loop but allows the event loop functions to be
reused in other contexts.
Diffstat (limited to 'src/nvim/event')
-rw-r--r-- | src/nvim/event/loop.c | 137 | ||||
-rw-r--r-- | src/nvim/event/loop.h | 55 | ||||
-rw-r--r-- | src/nvim/event/signal.c | 52 | ||||
-rw-r--r-- | src/nvim/event/signal.h | 22 | ||||
-rw-r--r-- | src/nvim/event/time.c | 55 | ||||
-rw-r--r-- | src/nvim/event/time.h | 20 |
6 files changed, 341 insertions, 0 deletions
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c new file mode 100644 index 0000000000..c467ae8b96 --- /dev/null +++ b/src/nvim/event/loop.c @@ -0,0 +1,137 @@ +#include <stdint.h> + +#include <uv.h> + +#include "nvim/event/loop.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/loop.c.generated.h" +#endif + + +void loop_init(Loop *loop, void *data) +{ + uv_loop_init(&loop->uv); + loop->uv.data = loop; + loop->deferred_events = kl_init(Event); + loop->immediate_events = kl_init(Event); +} + +void loop_poll_events(Loop *loop, int ms) +{ + static int recursive = 0; + + if (recursive++) { + abort(); // Should not re-enter uv_run + } + + bool wait = true; + uv_timer_t timer; + + if (ms > 0) { + uv_timer_init(&loop->uv, &timer); + // Use a repeating timeout of ms milliseconds to make sure + // we do not block indefinitely for I/O. + uv_timer_start(&timer, timer_cb, (uint64_t)ms, (uint64_t)ms); + } 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. + wait = false; + } + + if (wait) { + loop_run_once(loop); + } else { + loop_run_nowait(loop); + } + + if (ms > 0) { + // Ensure the timer handle is closed and run the event loop + // once more to let libuv perform it's cleanup + uv_timer_stop(&timer); + uv_close((uv_handle_t *)&timer, NULL); + loop_run_nowait(loop); + } + + recursive--; // Can re-enter uv_run now + process_events_from(loop->immediate_events); +} + +bool loop_has_deferred_events(Loop *loop) +{ + return loop->deferred_events_allowed && !kl_empty(loop->deferred_events); +} + +void loop_enable_deferred_events(Loop *loop) +{ + ++loop->deferred_events_allowed; +} + +void loop_disable_deferred_events(Loop *loop) +{ + --loop->deferred_events_allowed; +} + +// Queue an event +void loop_push_event(Loop *loop, Event event, bool deferred) +{ + // Sometimes libuv will run pending callbacks(timer for example) before + // blocking for a poll. If this happens and the callback pushes a event to one + // of the queues, the event would only be processed after the poll + // returns(user hits a key for example). To avoid this scenario, we call + // uv_stop when a event is enqueued. + loop_stop(loop); + kl_push(Event, deferred ? loop->deferred_events : loop->immediate_events, + event); +} + +void loop_process_event(Loop *loop) +{ + process_events_from(loop->deferred_events); +} + + +void loop_run(Loop *loop) +{ + uv_run(&loop->uv, UV_RUN_DEFAULT); +} + +void loop_run_once(Loop *loop) +{ + uv_run(&loop->uv, UV_RUN_ONCE); +} + +void loop_run_nowait(Loop *loop) +{ + uv_run(&loop->uv, UV_RUN_NOWAIT); +} + +void loop_stop(Loop *loop) +{ + uv_stop(&loop->uv); +} + +void loop_close(Loop *loop) +{ + do { + uv_run(&loop->uv, UV_RUN_DEFAULT); + } while (uv_loop_close(&loop->uv)); +} + +void loop_process_all_events(Loop *loop) +{ + process_events_from(loop->immediate_events); + process_events_from(loop->deferred_events); +} + +static void process_events_from(klist_t(Event) *queue) +{ + while (!kl_empty(queue)) { + Event event = kl_shift(Event, queue); + event.handler(event); + } +} + +static void timer_cb(uv_timer_t *handle) +{ +} diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h new file mode 100644 index 0000000000..e5a890dddb --- /dev/null +++ b/src/nvim/event/loop.h @@ -0,0 +1,55 @@ +#ifndef NVIM_EVENT_LOOP_H +#define NVIM_EVENT_LOOP_H + +#include <stdint.h> + +#include <uv.h> + +#include "nvim/lib/klist.h" +#include "nvim/os/time.h" + +typedef struct event Event; +typedef void (*event_handler)(Event event); + +struct event { + void *data; + event_handler handler; +}; + +typedef void * WatcherPtr; + +#define _noop(x) +KLIST_INIT(WatcherPtr, WatcherPtr, _noop) +KLIST_INIT(Event, Event, _noop) + +typedef struct loop { + uv_loop_t uv; + klist_t(Event) *deferred_events, *immediate_events; + int deferred_events_allowed; +} Loop; + +// Poll for events until a condition or timeout +#define LOOP_POLL_EVENTS_UNTIL(loop, timeout, condition) \ + do { \ + int remaining = timeout; \ + uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ + while (!(condition)) { \ + loop_poll_events(loop, remaining); \ + if (remaining == 0) { \ + break; \ + } else if (remaining > 0) { \ + uint64_t now = os_hrtime(); \ + remaining -= (int) ((now - before) / 1000000); \ + before = now; \ + if (remaining <= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/loop.h.generated.h" +#endif + +#endif // NVIM_EVENT_LOOP_H diff --git a/src/nvim/event/signal.c b/src/nvim/event/signal.c new file mode 100644 index 0000000000..63133b4f57 --- /dev/null +++ b/src/nvim/event/signal.c @@ -0,0 +1,52 @@ +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/signal.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/signal.c.generated.h" +#endif + + +void signal_watcher_init(Loop *loop, SignalWatcher *watcher, void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) +{ + uv_signal_init(&loop->uv, &watcher->uv); + watcher->uv.data = watcher; + watcher->data = data; + watcher->cb = NULL; +} + +void signal_watcher_start(SignalWatcher *watcher, signal_cb cb, int signum) + FUNC_ATTR_NONNULL_ALL +{ + watcher->cb = cb; + uv_signal_start(&watcher->uv, signal_watcher_cb, signum); +} + +void signal_watcher_stop(SignalWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + uv_signal_stop(&watcher->uv); +} + +void signal_watcher_close(SignalWatcher *watcher, signal_close_cb cb) + FUNC_ATTR_NONNULL_ARG(1) +{ + watcher->close_cb = cb; + uv_close((uv_handle_t *)&watcher->uv, close_cb); +} + +static void signal_watcher_cb(uv_signal_t *handle, int signum) +{ + SignalWatcher *watcher = handle->data; + watcher->cb(watcher, signum, watcher->data); +} + +static void close_cb(uv_handle_t *handle) +{ + SignalWatcher *watcher = handle->data; + if (watcher->close_cb) { + watcher->close_cb(watcher, watcher->data); + } +} diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h new file mode 100644 index 0000000000..c269fa9d95 --- /dev/null +++ b/src/nvim/event/signal.h @@ -0,0 +1,22 @@ +#ifndef NVIM_EVENT_SIGNAL_H +#define NVIM_EVENT_SIGNAL_H + +#include <uv.h> + +#include "nvim/event/loop.h" + +typedef struct signal_watcher SignalWatcher; +typedef void (*signal_cb)(SignalWatcher *watcher, int signum, void *data); +typedef void (*signal_close_cb)(SignalWatcher *watcher, void *data); + +struct signal_watcher { + uv_signal_t uv; + void *data; + signal_cb cb; + signal_close_cb close_cb; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/signal.h.generated.h" +#endif +#endif // NVIM_EVENT_SIGNAL_H diff --git a/src/nvim/event/time.c b/src/nvim/event/time.c new file mode 100644 index 0000000000..ce33cdfc10 --- /dev/null +++ b/src/nvim/event/time.c @@ -0,0 +1,55 @@ +#include <stdint.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/time.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/time.c.generated.h" +#endif + + +void time_watcher_init(Loop *loop, TimeWatcher *watcher, void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) +{ + uv_timer_init(&loop->uv, &watcher->uv); + watcher->uv.data = watcher; + watcher->data = data; +} + +void time_watcher_start(TimeWatcher *watcher, time_cb cb, uint64_t timeout, + uint64_t repeat) + FUNC_ATTR_NONNULL_ALL +{ + watcher->cb = cb; + uv_timer_start(&watcher->uv, time_watcher_cb, timeout, repeat); +} + +void time_watcher_stop(TimeWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + uv_timer_stop(&watcher->uv); +} + +void time_watcher_close(TimeWatcher *watcher, time_cb cb) + FUNC_ATTR_NONNULL_ARG(1) +{ + watcher->close_cb = cb; + uv_close((uv_handle_t *)&watcher->uv, close_cb); +} + +static void time_watcher_cb(uv_timer_t *handle) + FUNC_ATTR_NONNULL_ALL +{ + TimeWatcher *watcher = handle->data; + watcher->cb(watcher, watcher->data); +} + +static void close_cb(uv_handle_t *handle) +{ + TimeWatcher *watcher = handle->data; + if (watcher->close_cb) { + watcher->close_cb(watcher, watcher->data); + } +} diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h new file mode 100644 index 0000000000..ee50e53d11 --- /dev/null +++ b/src/nvim/event/time.h @@ -0,0 +1,20 @@ +#ifndef NVIM_EVENT_TIME_H +#define NVIM_EVENT_TIME_H + +#include <uv.h> + +#include "nvim/event/loop.h" + +typedef struct time_watcher TimeWatcher; +typedef void (*time_cb)(TimeWatcher *watcher, void *data); + +struct time_watcher { + uv_timer_t uv; + void *data; + time_cb cb, close_cb; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/time.h.generated.h" +#endif +#endif // NVIM_EVENT_TIME_H |