aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/event
diff options
context:
space:
mode:
authorThiago de Arruda <tpadilha84@gmail.com>2015-07-16 23:10:04 -0300
committerThiago de Arruda <tpadilha84@gmail.com>2015-07-17 00:19:19 -0300
commit991d3ec1e679bb6407f2a5820910d2968424183c (patch)
tree38810fb657e1e1ea9d77b7a7963e874e8bda99d1 /src/nvim/event
parent9e42ef4e1312fb6888d2691e9d979b95dd43ec94 (diff)
downloadrneovim-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.c137
-rw-r--r--src/nvim/event/loop.h55
-rw-r--r--src/nvim/event/signal.c52
-rw-r--r--src/nvim/event/signal.h22
-rw-r--r--src/nvim/event/time.c55
-rw-r--r--src/nvim/event/time.h20
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