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/api/vim.c | 21 ++++++++++++++++ src/nvim/eval.c | 55 ++++++------------------------------------ src/nvim/event/loop.c | 3 +-- src/nvim/event/multiqueue.c | 35 +++++++++++++++++++++++++++ src/nvim/memory.c | 4 --- src/nvim/msgpack_rpc/channel.c | 10 ++++++++ src/nvim/msgpack_rpc/helpers.c | 2 +- src/nvim/normal.c | 5 ++-- src/nvim/os/input.c | 16 ++++++++++++ src/nvim/state.c | 49 +++++++++++++++++++++++++++++++++++++ src/nvim/tui/tui.c | 3 --- 11 files changed, 143 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index da00fbc6e3..7c57a5b456 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -11,6 +11,7 @@ #include "nvim/api/vim.h" #include "nvim/ascii.h" +#include "nvim/log.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/api/buffer.h" @@ -27,6 +28,7 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/option.h" +#include "nvim/state.h" #include "nvim/syntax.h" #include "nvim/getchar.h" #include "nvim/os/input.h" @@ -701,6 +703,25 @@ Dictionary nvim_get_color_map(void) } +/// Gets the current mode. +/// mode: Mode string. |mode()| +/// blocking: true if Nvim is waiting for input. +/// +/// @returns Dictionary { "mode": String, "blocking": Boolean } +Dictionary nvim_get_mode(void) + FUNC_API_SINCE(2) FUNC_API_ASYNC +{ + Dictionary rv = ARRAY_DICT_INIT; + char *modestr = get_mode(); + bool blocked = input_blocking(); + ILOG("blocked=%d", blocked); + + PUT(rv, "mode", STRING_OBJ(cstr_as_string(modestr))); + PUT(rv, "blocking", BOOLEAN_OBJ(blocked)); + + return rv; +} + Array nvim_get_api_info(uint64_t channel_id) FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_NOEVAL { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c02d172458..b0f47d8e45 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12575,59 +12575,18 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "mode()" function - */ +/// "mode()" function static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[3]; + char *mode = get_mode(); - buf[1] = NUL; - buf[2] = NUL; - - if (VIsual_active) { - if (VIsual_select) - buf[0] = VIsual_mode + 's' - 'v'; - else - buf[0] = VIsual_mode; - } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE - || State == CONFIRM) { - buf[0] = 'r'; - if (State == ASKMORE) - buf[1] = 'm'; - else if (State == CONFIRM) - buf[1] = '?'; - } else if (State == EXTERNCMD) - buf[0] = '!'; - else if (State & INSERT) { - if (State & VREPLACE_FLAG) { - buf[0] = 'R'; - buf[1] = 'v'; - } else if (State & REPLACE_FLAG) - buf[0] = 'R'; - else - buf[0] = 'i'; - } else if (State & CMDLINE) { - buf[0] = 'c'; - if (exmode_active) - buf[1] = 'v'; - } else if (exmode_active) { - buf[0] = 'c'; - buf[1] = 'e'; - } else if (State & TERM_FOCUS) { - buf[0] = 't'; - } else { - buf[0] = 'n'; - if (finish_op) - buf[1] = 'o'; + // Clear out the minor mode when the argument is not a non-zero number or + // non-empty string. + if (!non_zero_arg(&argvars[0])) { + mode[1] = NUL; } - /* Clear out the minor mode when the argument is not a non-zero number or - * non-empty string. */ - if (!non_zero_arg(&argvars[0])) - buf[1] = NUL; - - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = (char_u *)mode; rettv->v_type = VAR_STRING; } 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) { diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 0ee4776581..74c58fb203 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -345,10 +345,6 @@ char *xstpcpy(char *restrict dst, const char *restrict src) /// WARNING: xstpncpy will ALWAYS write maxlen bytes. If src is shorter than /// maxlen, zeroes will be written to the remaining bytes. /// -/// TODO(aktau): I don't see a good reason to have this last behaviour, and -/// it is potentially wasteful. Could we perhaps deviate from the standard -/// and not zero the rest of the buffer? -/// /// @param dst /// @param src /// @param maxlen diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 59594357de..799e6eadef 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -28,7 +28,9 @@ #include "nvim/map.h" #include "nvim/log.h" #include "nvim/misc1.h" +#include "nvim/state.h" #include "nvim/lib/kvec.h" +#include "nvim/os/input.h" #define CHANNEL_BUFFER_SIZE 0xffff @@ -433,6 +435,14 @@ static void handle_request(Channel *channel, msgpack_object *request) handler.async = true; } + if (handler.async) { + char buf[256] = { 0 }; + memcpy(buf, method->via.bin.ptr, MIN(255, method->via.bin.size)); + if (strcmp("nvim_get_mode", buf) == 0) { + handler.async = input_blocking(); + } + } + RequestEvent *event_data = xmalloc(sizeof(RequestEvent)); event_data->channel = channel; event_data->handler = handler; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 0228582d37..91ef5524ea 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -76,7 +76,7 @@ typedef struct { size_t idx; } MPToAPIObjectStackItem; -/// Convert type used by msgpack parser to Neovim own API type +/// Convert type used by msgpack parser to Nvim API type. /// /// @param[in] obj Msgpack value to convert. /// @param[out] arg Location where result of conversion will be saved. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 51da9429b6..09ad7beb4b 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -14,6 +14,7 @@ #include #include "nvim/vim.h" +#include "nvim/log.h" #include "nvim/ascii.h" #include "nvim/normal.h" #include "nvim/buffer.h" @@ -541,7 +542,7 @@ static bool normal_handle_special_visual_command(NormalState *s) return false; } -static bool normal_need_aditional_char(NormalState *s) +static bool normal_need_additional_char(NormalState *s) { int flags = nv_cmds[s->idx].cmd_flags; bool pending_op = s->oa.op_type != OP_NOP; @@ -1083,7 +1084,7 @@ static int normal_execute(VimState *state, int key) } // Get an additional character if we need one. - if (normal_need_aditional_char(s)) { + if (normal_need_additional_char(s)) { normal_get_additional_char(s); } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 7b5e14dd19..26f2be6c02 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -23,6 +23,7 @@ #include "nvim/main.h" #include "nvim/misc1.h" #include "nvim/state.h" +#include "nvim/log.h" #define READ_BUFFER_SIZE 0xfff #define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4) @@ -38,6 +39,7 @@ static RBuffer *input_buffer = NULL; static bool input_eof = false; static int global_fd = 0; static int events_enabled = 0; +static bool blocking = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/input.c.generated.h" @@ -327,13 +329,27 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, return bufsize; } +/// @return true if the main loop is blocked and waiting for input. +bool input_blocking(void) +{ + return blocking; +} + static bool input_poll(int ms) { if (do_profiling == PROF_YES && ms) { prof_inchar_enter(); } + if ((ms == - 1 || ms > 0) + && !(events_enabled || input_ready() || input_eof) + ) { + blocking = true; + multiqueue_process_debug(main_loop.events); + multiqueue_process_events(main_loop.events); + } LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof); + blocking = false; if (do_profiling == PROF_YES && ms) { prof_inchar_exit(); diff --git a/src/nvim/state.c b/src/nvim/state.c index 210708c3f4..be6aa21664 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -98,3 +98,52 @@ int get_real_state(void) return State; } +/// @returns[allocated] mode string +char *get_mode(void) +{ + char *buf = xcalloc(3, sizeof(char)); + + if (VIsual_active) { + if (VIsual_select) { + buf[0] = (char)(VIsual_mode + 's' - 'v'); + } else { + buf[0] = (char)VIsual_mode; + } + } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE + || State == CONFIRM) { + buf[0] = 'r'; + if (State == ASKMORE) { + buf[1] = 'm'; + } else if (State == CONFIRM) { + buf[1] = '?'; + } + } else if (State == EXTERNCMD) { + buf[0] = '!'; + } else if (State & INSERT) { + if (State & VREPLACE_FLAG) { + buf[0] = 'R'; + buf[1] = 'v'; + } else if (State & REPLACE_FLAG) { + buf[0] = 'R'; + } else { + buf[0] = 'i'; + } + } else if (State & CMDLINE) { + buf[0] = 'c'; + if (exmode_active) { + buf[1] = 'v'; + } + } else if (exmode_active) { + buf[0] = 'c'; + buf[1] = 'e'; + } else if (State & TERM_FOCUS) { + buf[0] = 't'; + } else { + buf[0] = 'n'; + if (finish_op) { + buf[1] = 'o'; + } + } + + return buf; +} diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 21abc19c47..356221f7ce 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -74,9 +74,6 @@ typedef struct { bool out_isatty; SignalWatcher winch_handle, cont_handle; bool cont_received; - // Event scheduled by the ui bridge. Since the main thread suspends until - // the event is handled, it is fine to use a single field instead of a queue - Event scheduled_event; UGrid grid; kvec_t(Rect) invalid_regions; int out_fd; -- 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/api/vim.c | 1 - src/nvim/event/defs.h | 5 +++ src/nvim/event/multiqueue.c | 93 +++++++++++++++++++++++------------------- src/nvim/event/multiqueue.h | 2 +- src/nvim/msgpack_rpc/channel.c | 32 ++++++++------- src/nvim/os/input.c | 10 ++--- 6 files changed, 78 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 7c57a5b456..a00afc24fa 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -714,7 +714,6 @@ Dictionary nvim_get_mode(void) Dictionary rv = ARRAY_DICT_INIT; char *modestr = get_mode(); bool blocked = input_blocking(); - ILOG("blocked=%d", blocked); PUT(rv, "mode", STRING_OBJ(cstr_as_string(modestr))); PUT(rv, "blocking", BOOLEAN_OBJ(blocked)); 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 diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 799e6eadef..9e43924db1 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -435,24 +435,26 @@ static void handle_request(Channel *channel, msgpack_object *request) handler.async = true; } - if (handler.async) { - char buf[256] = { 0 }; - memcpy(buf, method->via.bin.ptr, MIN(255, method->via.bin.size)); - if (strcmp("nvim_get_mode", buf) == 0) { - handler.async = input_blocking(); - } - } - - RequestEvent *event_data = xmalloc(sizeof(RequestEvent)); - event_data->channel = channel; - event_data->handler = handler; - event_data->args = args; - event_data->request_id = request_id; + RequestEvent *evdata = xmalloc(sizeof(RequestEvent)); + evdata->channel = channel; + evdata->handler = handler; + evdata->args = args; + evdata->request_id = request_id; incref(channel); if (handler.async) { - on_request_event((void **)&event_data); + bool is_get_mode = sizeof("nvim_get_mode") - 1 == method->via.bin.size + && !strncmp("nvim_get_mode", method->via.bin.ptr, method->via.bin.size); + + if (is_get_mode && !input_blocking()) { + // Schedule on the main loop with special priority. #6247 + Event ev = event_create(kEvPriorityAsync, on_request_event, 1, evdata); + multiqueue_put_event(channel->events, ev); + } else { + // Invoke immediately. + on_request_event((void **)&evdata); + } } else { - multiqueue_put(channel->events, on_request_event, 1, event_data); + multiqueue_put(channel->events, on_request_event, 1, evdata); } } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 26f2be6c02..de6d4a9010 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -341,12 +341,12 @@ static bool input_poll(int ms) prof_inchar_enter(); } - if ((ms == - 1 || ms > 0) - && !(events_enabled || input_ready() || input_eof) - ) { + if ((ms == - 1 || ms > 0) && !events_enabled && !input_eof) { + // We have discovered that the pending input will provoke a blocking wait. + // Process any events marked with priority `kEvPriorityAsync`: these events + // must be handled after flushing input. See channel.c:handle_request #6247 blocking = true; - multiqueue_process_debug(main_loop.events); - multiqueue_process_events(main_loop.events); + multiqueue_process_priority(main_loop.events, kEvPriorityAsync); } LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof); blocking = false; -- 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/api/vim.c | 1 - src/nvim/event/multiqueue.c | 1 - src/nvim/msgpack_rpc/channel.c | 7 +++---- src/nvim/msgpack_rpc/channel.h | 5 +++++ src/nvim/normal.c | 1 - src/nvim/os/input.c | 8 +++----- 6 files changed, 11 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index a00afc24fa..11f15b5ad1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -11,7 +11,6 @@ #include "nvim/api/vim.h" #include "nvim/ascii.h" -#include "nvim/log.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/api/buffer.h" 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; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 9e43924db1..911f2a6fa4 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -28,7 +28,6 @@ #include "nvim/map.h" #include "nvim/log.h" #include "nvim/misc1.h" -#include "nvim/state.h" #include "nvim/lib/kvec.h" #include "nvim/os/input.h" @@ -91,6 +90,7 @@ static msgpack_sbuffer out_buffer; /// Initializes the module void channel_init(void) { + ch_before_blocking_events = multiqueue_new_child(main_loop.events); channels = pmap_new(uint64_t)(); event_strings = pmap_new(cstr_t)(); msgpack_sbuffer_init(&out_buffer); @@ -446,9 +446,8 @@ static void handle_request(Channel *channel, msgpack_object *request) && !strncmp("nvim_get_mode", method->via.bin.ptr, method->via.bin.size); if (is_get_mode && !input_blocking()) { - // Schedule on the main loop with special priority. #6247 - Event ev = event_create(kEvPriorityAsync, on_request_event, 1, evdata); - multiqueue_put_event(channel->events, ev); + // Defer the event to a special queue used by os/input.c. #6247 + multiqueue_put(ch_before_blocking_events, on_request_event, 1, evdata); } else { // Invoke immediately. on_request_event((void **)&evdata); diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h index 0d92976d02..f8fe6f129b 100644 --- a/src/nvim/msgpack_rpc/channel.h +++ b/src/nvim/msgpack_rpc/channel.h @@ -11,6 +11,11 @@ #define METHOD_MAXLEN 512 +/// HACK: os/input.c drains this queue immediately before blocking for input. +/// Events on this queue are async-safe, but they need the resolved state +/// of os_inchar(), so they are processed "just-in-time". +MultiQueue *ch_before_blocking_events; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/channel.h.generated.h" #endif diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 09ad7beb4b..f73e3079b9 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -14,7 +14,6 @@ #include #include "nvim/vim.h" -#include "nvim/log.h" #include "nvim/ascii.h" #include "nvim/normal.h" #include "nvim/buffer.h" diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index de6d4a9010..31e06ce404 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -23,7 +23,7 @@ #include "nvim/main.h" #include "nvim/misc1.h" #include "nvim/state.h" -#include "nvim/log.h" +#include "nvim/msgpack_rpc/channel.h" #define READ_BUFFER_SIZE 0xfff #define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4) @@ -342,11 +342,9 @@ static bool input_poll(int ms) } if ((ms == - 1 || ms > 0) && !events_enabled && !input_eof) { - // We have discovered that the pending input will provoke a blocking wait. - // Process any events marked with priority `kEvPriorityAsync`: these events - // must be handled after flushing input. See channel.c:handle_request #6247 + // The pending input provoked a blocking wait. Do special events now. #6247 blocking = true; - multiqueue_process_priority(main_loop.events, kEvPriorityAsync); + multiqueue_process_events(ch_before_blocking_events); } LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof); blocking = false; -- 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 +- src/nvim/message.c | 2 +- src/nvim/tui/input.c | 4 ++-- src/nvim/tui/tui.c | 2 +- src/nvim/ui.c | 2 +- src/nvim/ui_bridge.c | 4 ++-- 8 files changed, 11 insertions(+), 45 deletions(-) (limited to 'src') 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 diff --git a/src/nvim/message.c b/src/nvim/message.c index 146937c25a..696855e3aa 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -604,7 +604,7 @@ void msg_schedule_emsgf(const char *const fmt, ...) va_end(ap); char *s = xstrdup((char *)IObuff); - loop_schedule(&main_loop, event_create(1, msg_emsgf_event, 1, s)); + loop_schedule(&main_loop, event_create(msg_emsgf_event, 1, s)); } /* diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index b86ab8cf2f..3d37fabeee 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -102,7 +102,7 @@ static void flush_input(TermInput *input, bool wait_until_empty) size_t drain_boundary = wait_until_empty ? 0 : 0xff; do { uv_mutex_lock(&input->key_buffer_mutex); - loop_schedule(&main_loop, event_create(1, wait_input_enqueue, 1, input)); + loop_schedule(&main_loop, event_create(wait_input_enqueue, 1, input)); input->waiting = true; while (input->waiting) { uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex); @@ -352,7 +352,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, stream_close(&input->read_stream, NULL, NULL); multiqueue_put(input->loop->fast_events, restart_reading, 1, input); } else { - loop_schedule(&main_loop, event_create(1, input_done_event, 0)); + loop_schedule(&main_loop, event_create(input_done_event, 0)); } return; } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 356221f7ce..5653924154 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -773,7 +773,7 @@ static void tui_suspend(UI *ui) // before continuing. This is done in another callback to avoid // loop_poll_events recursion multiqueue_put_event(data->loop->fast_events, - event_create(1, suspend_event, 1, ui)); + event_create(suspend_event, 1, ui)); } static void tui_set_title(UI *ui, char *title) diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 713dffb46c..924a4192bc 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -198,7 +198,7 @@ static void ui_refresh_event(void **argv) void ui_schedule_refresh(void) { - loop_schedule(&main_loop, event_create(1, ui_refresh_event, 0)); + loop_schedule(&main_loop, event_create(ui_refresh_event, 0)); } void ui_resize(int new_width, int new_height) diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index b7b12ae39e..d790770892 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -40,13 +40,13 @@ static argv_callback uilog_event = NULL; uilog_event = ui_bridge_##name##_event; \ } \ ((UIBridgeData *)ui)->scheduler( \ - event_create(1, ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \ + event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \ } while (0) #else // Schedule a function call on the UI bridge thread. #define UI_CALL(ui, name, argc, ...) \ ((UIBridgeData *)ui)->scheduler( \ - event_create(1, ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) + event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) #endif #define INT2PTR(i) ((void *)(uintptr_t)i) -- cgit