From 3ea10077534cb1dcb1597ffcf85e601fa0c0e27b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 13 Mar 2017 15:02:37 +0100 Subject: api: nvim_get_mode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asynchronous API functions are served immediately, which means pending input could change the state of Nvim shortly after an async API function result is returned. nvim_get_mode() is different: - If RPCs are known to be blocked, it responds immediately (without flushing the input/event queue) - else it is handled just-in-time before waiting for input, after pending input was processed. This makes the result more reliable (but not perfect). Internally this is handled as a special case, but _semantically_ nothing has changed: API users never know when input flushes, so this internal special-case doesn't violate that. As far as API users are concerned, nvim_get_mode() is just another asynchronous API function. In all cases nvim_get_mode() never blocks for more than the time it takes to flush the input/event queue (~µs). Note: This doesn't address #6166; nvim_get_mode() will provoke #6166 if e.g. `d` is operator-pending. Closes #6159 --- src/nvim/event/loop.c | 3 +-- src/nvim/event/multiqueue.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) (limited to 'src/nvim/event') diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 6963978581..c709ce9a1c 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -44,8 +44,7 @@ void loop_poll_events(Loop *loop, int ms) // we do not block indefinitely for I/O. uv_timer_start(&loop->poll_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. + // For ms == 0, do a non-blocking event poll. mode = UV_RUN_NOWAIT; } diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index a17bae31e3..b144347fdb 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -55,6 +55,7 @@ #include "nvim/event/multiqueue.h" #include "nvim/memory.h" +#include "nvim/log.h" #include "nvim/os/time.h" typedef struct multiqueue_item MultiQueueItem; @@ -151,6 +152,40 @@ void multiqueue_process_events(MultiQueue *this) } } +void multiqueue_process_debug(MultiQueue *this) +{ + assert(this); + QUEUE *start = QUEUE_HEAD(&this->headtail); + QUEUE *cur = start; + // MultiQueue *start = this; + // MultiQueue *cur = start; + do { + MultiQueueItem *item = multiqueue_node_data(cur); + Event ev; + if (item->link) { + assert(!this->parent); + // get the next node in the linked queue + MultiQueue *linked = item->data.queue; + assert(!multiqueue_empty(linked)); + MultiQueueItem *child = + multiqueue_node_data(QUEUE_HEAD(&linked->headtail)); + ev = child->data.item.event; + } else { + ev = item->data.item.event; + } + + // Event event = multiqueue_get(this); + // if (event.handler) { + // event.handler(event.argv); + // } + + ILOG("ev: priority=%d, handler=%p arg1=%s", ev.priority, ev.handler, + ev.argv ? ev.argv[0] : "(null)"); + + cur = cur->next; + } while (cur && cur != start); +} + /// Removes all events without processing them. void multiqueue_purge_events(MultiQueue *this) { -- cgit From acfd2a2a29ae852ecc965ca888eb5049400bf39d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 14 Mar 2017 00:44:03 +0100 Subject: input.c: Process only safe events before blocking. Introduce multiqueue_process_priority() to process only events at or above a certain priority. --- src/nvim/event/defs.h | 5 +++ src/nvim/event/multiqueue.c | 93 ++++++++++++++++++++++++--------------------- src/nvim/event/multiqueue.h | 2 +- 3 files changed, 56 insertions(+), 44 deletions(-) (limited to 'src/nvim/event') diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h index e5335d9f25..509a3f8e7f 100644 --- a/src/nvim/event/defs.h +++ b/src/nvim/event/defs.h @@ -6,6 +6,11 @@ #define EVENT_HANDLER_MAX_ARGC 6 +typedef enum { + kEvPriorityNormal = 1, + kEvPriorityAsync = 2, // safe to run in any state +} EventPriority; + typedef void (*argv_callback)(void **argv); typedef struct message { int priority; diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index b144347fdb..a479a032f4 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -127,6 +127,7 @@ void multiqueue_free(MultiQueue *this) xfree(this); } +/// Removes the next item and returns its Event. Event multiqueue_get(MultiQueue *this) { return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this); @@ -145,45 +146,38 @@ void multiqueue_process_events(MultiQueue *this) { assert(this); while (!multiqueue_empty(this)) { - Event event = multiqueue_get(this); + Event event = multiqueue_remove(this); if (event.handler) { event.handler(event.argv); } } } -void multiqueue_process_debug(MultiQueue *this) +void multiqueue_process_priority(MultiQueue *this, int priority) { assert(this); QUEUE *start = QUEUE_HEAD(&this->headtail); - QUEUE *cur = start; - // MultiQueue *start = this; - // MultiQueue *cur = start; - do { + QUEUE *cur = start; + while (!multiqueue_empty(this)) { MultiQueueItem *item = multiqueue_node_data(cur); - Event ev; - if (item->link) { - assert(!this->parent); - // get the next node in the linked queue - MultiQueue *linked = item->data.queue; - assert(!multiqueue_empty(linked)); - MultiQueueItem *child = - multiqueue_node_data(QUEUE_HEAD(&linked->headtail)); - ev = child->data.item.event; + assert(!item->link || !this->parent); // Only a parent queue has link-nodes + Event ev = multiqueueitem_get_event(item, false); + + if (ev.priority >= priority) { + if (ev.handler) { + ev.handler(ev.argv); + } + // Processed. Remove this item and get the new head. + (void)multiqueue_remove(this); + cur = QUEUE_HEAD(&this->headtail); } else { - ev = item->data.item.event; + // Not processed. Skip this item and get the next one. + cur = cur->next->next; + if (!cur || cur == start) { + break; + } } - - // Event event = multiqueue_get(this); - // if (event.handler) { - // event.handler(event.argv); - // } - - ILOG("ev: priority=%d, handler=%p arg1=%s", ev.priority, ev.handler, - ev.argv ? ev.argv[0] : "(null)"); - - cur = cur->next; - } while (cur && cur != start); + } } /// Removes all events without processing them. @@ -213,36 +207,48 @@ size_t multiqueue_size(MultiQueue *this) return this->size; } -static Event multiqueue_remove(MultiQueue *this) +/// Gets an Event from an item. +/// +/// @param remove Remove the node from its queue, and free it. +static Event multiqueueitem_get_event(MultiQueueItem *item, bool remove) { - assert(!multiqueue_empty(this)); - QUEUE *h = QUEUE_HEAD(&this->headtail); - QUEUE_REMOVE(h); - MultiQueueItem *item = multiqueue_node_data(h); - Event rv; - + assert(item != NULL); + Event ev; if (item->link) { - assert(!this->parent); - // remove the next node in the linked queue + // get the next node in the linked queue MultiQueue *linked = item->data.queue; assert(!multiqueue_empty(linked)); MultiQueueItem *child = multiqueue_node_data(QUEUE_HEAD(&linked->headtail)); - QUEUE_REMOVE(&child->node); - rv = child->data.item.event; - xfree(child); + ev = child->data.item.event; + // remove the child node + if (remove) { + QUEUE_REMOVE(&child->node); + xfree(child); + } } else { - if (this->parent) { - // remove the corresponding link node in the parent queue + // remove the corresponding link node in the parent queue + if (remove && item->data.item.parent_item) { QUEUE_REMOVE(&item->data.item.parent_item->node); xfree(item->data.item.parent_item); + item->data.item.parent_item = NULL; } - rv = item->data.item.event; + ev = item->data.item.event; } + return ev; +} +static Event multiqueue_remove(MultiQueue *this) +{ + assert(!multiqueue_empty(this)); + QUEUE *h = QUEUE_HEAD(&this->headtail); + QUEUE_REMOVE(h); + MultiQueueItem *item = multiqueue_node_data(h); + assert(!item->link || !this->parent); // Only a parent queue has link-nodes + Event ev = multiqueueitem_get_event(item, true); this->size--; xfree(item); - return rv; + return ev; } static void multiqueue_push(MultiQueue *this, Event event) @@ -250,6 +256,7 @@ static void multiqueue_push(MultiQueue *this, Event event) MultiQueueItem *item = xmalloc(sizeof(MultiQueueItem)); item->link = false; item->data.item.event = event; + item->data.item.parent_item = NULL; QUEUE_INSERT_TAIL(&this->headtail, &item->node); if (this->parent) { // push link node to the parent queue diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h index def6b95a10..72145482aa 100644 --- a/src/nvim/event/multiqueue.h +++ b/src/nvim/event/multiqueue.h @@ -10,7 +10,7 @@ typedef struct multiqueue MultiQueue; typedef void (*put_callback)(MultiQueue *multiq, void *data); #define multiqueue_put(q, h, ...) \ - multiqueue_put_event(q, event_create(1, h, __VA_ARGS__)); + multiqueue_put_event(q, event_create(kEvPriorityNormal, h, __VA_ARGS__)); #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From f17a8185191b778960953508a5bf9b5f95b0560c Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 27 Apr 2017 13:54:54 +0200 Subject: api/nvim_get_mode: Use child-queue instead of "priority". --- src/nvim/event/multiqueue.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/event') diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index a479a032f4..66a261b554 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -55,7 +55,6 @@ #include "nvim/event/multiqueue.h" #include "nvim/memory.h" -#include "nvim/log.h" #include "nvim/os/time.h" typedef struct multiqueue_item MultiQueueItem; -- cgit From 8f59d1483934f91011b755406251136c406e77f6 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 27 Apr 2017 14:38:41 +0200 Subject: event: Remove "priority" concept. It was replaced by the "child queue" concept (MultiQueue). --- src/nvim/event/defs.h | 13 +++---------- src/nvim/event/multiqueue.c | 27 --------------------------- src/nvim/event/multiqueue.h | 2 +- 3 files changed, 4 insertions(+), 38 deletions(-) (limited to 'src/nvim/event') diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h index 509a3f8e7f..cc875d74b9 100644 --- a/src/nvim/event/defs.h +++ b/src/nvim/event/defs.h @@ -6,23 +6,16 @@ #define EVENT_HANDLER_MAX_ARGC 6 -typedef enum { - kEvPriorityNormal = 1, - kEvPriorityAsync = 2, // safe to run in any state -} EventPriority; - typedef void (*argv_callback)(void **argv); typedef struct message { - int priority; argv_callback handler; void *argv[EVENT_HANDLER_MAX_ARGC]; } Event; typedef void(*event_scheduler)(Event event, void *data); -#define VA_EVENT_INIT(event, p, h, a) \ +#define VA_EVENT_INIT(event, h, a) \ do { \ assert(a <= EVENT_HANDLER_MAX_ARGC); \ - (event)->priority = p; \ (event)->handler = h; \ if (a) { \ va_list args; \ @@ -34,11 +27,11 @@ typedef void(*event_scheduler)(Event event, void *data); } \ } while (0) -static inline Event event_create(int priority, argv_callback cb, int argc, ...) +static inline Event event_create(argv_callback cb, int argc, ...) { assert(argc <= EVENT_HANDLER_MAX_ARGC); Event event; - VA_EVENT_INIT(&event, priority, cb, argc); + VA_EVENT_INIT(&event, cb, argc); return event; } diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index 66a261b554..ef9f3f1870 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -152,33 +152,6 @@ void multiqueue_process_events(MultiQueue *this) } } -void multiqueue_process_priority(MultiQueue *this, int priority) -{ - assert(this); - QUEUE *start = QUEUE_HEAD(&this->headtail); - QUEUE *cur = start; - while (!multiqueue_empty(this)) { - MultiQueueItem *item = multiqueue_node_data(cur); - assert(!item->link || !this->parent); // Only a parent queue has link-nodes - Event ev = multiqueueitem_get_event(item, false); - - if (ev.priority >= priority) { - if (ev.handler) { - ev.handler(ev.argv); - } - // Processed. Remove this item and get the new head. - (void)multiqueue_remove(this); - cur = QUEUE_HEAD(&this->headtail); - } else { - // Not processed. Skip this item and get the next one. - cur = cur->next->next; - if (!cur || cur == start) { - break; - } - } - } -} - /// Removes all events without processing them. void multiqueue_purge_events(MultiQueue *this) { diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h index 72145482aa..a688107665 100644 --- a/src/nvim/event/multiqueue.h +++ b/src/nvim/event/multiqueue.h @@ -10,7 +10,7 @@ typedef struct multiqueue MultiQueue; typedef void (*put_callback)(MultiQueue *multiq, void *data); #define multiqueue_put(q, h, ...) \ - multiqueue_put_event(q, event_create(kEvPriorityNormal, h, __VA_ARGS__)); + multiqueue_put_event(q, event_create(h, __VA_ARGS__)); #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit