-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
[RFC] api: nvim_get_mode() #6247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/nvim/msgpack_rpc/channel.c
Outdated
char buf[256] = { 0 }; | ||
memcpy(buf, method->via.bin.ptr, MIN(255, method->via.bin.size)); | ||
bool is_get_mode = strcmp("nvim_get_mode", buf) == 0; | ||
handler.async = is_get_mode && input_blocking(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
special case nvim_get_mode
: If we know RPC will be blocked, it can (and must) be asynchronous.
src/nvim/os/input.c
Outdated
&& !(events_enabled || input_ready() || input_eof) | ||
) { | ||
blocking = true; | ||
multiqueue_process_events(main_loop.events); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We found out that the pending input will provoke a blocking wait, so here we process queued events (which may include the synchronous nvim_get_mode
) once before blocking.
It's been quite some time since I looked through the code, so I may be missing something and if so, please let me know. I don't think I understand this PR, why do you need a function marked with |
src/nvim/rbuffer.c
Outdated
{ | ||
return (size_t)(buf->end_ptr - buf->start_ptr); | ||
} | ||
|
||
size_t rbuffer_space(RBuffer *buf) FUNC_ATTR_NONNULL_ALL | ||
size_t rbuffer_space(RBuffer *buf) | ||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure it is safe to mark these functions as pure? Considering RBuffer
is used by both the main thread and the ui thread, the compiler may not be able to when RBuffer
is changed by another thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for mentioning that. I didn't know that pure is not allowed to be used if the parameter is used by multiple threads, that makes it extremely difficult to ever know if a function can safely be marked pure. @ZyX-I can you confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the compiler will consider the memory as changed if we use locks to access RBuffer
(which we do, if I remember correctly), but then this attribute won't be of much help. In any case, since the computation is very trivial, it is better to be safe than sorry(trying to debug it in a far future, where we forgot about this)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I'll remove this change. Just curious for future reference, though.
That is the goal, in particular the RPC client needs to know if the next synchronous RPC will block. There could be input in the queue which wasn't flushed yet, which will cause the next synchronous to RPC block. For example:
In the above sequence, if For single-threaded clients like actualvim, this is a problem because user input is sent by the client. If the client is blocked then it's stuck. Another use-case is the neovim test suite: currently the tests have no way to detect a press-enter prompt before it happens. |
src/nvim/os/input.c
Outdated
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it has been quite some time since I've maintained this code, I'm not sure what are the implications of adding this multiqueue_process_events()
call, but consider that input_poll
is called no matter what state nvim is in, so it may not be safe to process events.
In any case, this condition is the same as (ms == - 1 || ms > 0) && !events_enabled && !input_ready() && !input_eof
, but input_ready()
cannot be true for this function to be called(See input_poll
below), so it could be simplified to (ms == - 1 || ms > 0) && !events_enabled && !input_eof
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was afraid of that. The only other option I can think of is to manage the nvim_get_mode
requests in a child queue, and then process only those calls here. E.g.
channel_process_safe_events()
or something like that. And that would iterate through nvim_get_mode
events.
I thought maybe main_loop.fast_events
could be used for this purpose somehow, but didn't have success.
OK I understand the issue now. Even so, I'm unsure if this is the best approach. When I started the pushdown automaton refactor(@8d93621c6368fb6e3e3f7138bff50e08585f347b), the goal was to add explicit support for For now I suggest to investigate the amount of work required to add support for |
I didn't forget about the K_EVENT refactoring intentions, but that's a high-risk task. Adding a new API function is much lower risk, and also forward-compatible with whatever future refactoring may occur. We also need to un-block https://github.com/lunixbochs/actualvim (and who-knows-what else: this has been a known issue for ~2 years). |
Any comments on #6247 (comment) ? Could |
It may not be as high-risk as you think, I actually had written down a plan on how to incrementally move vim implicit modes into the
I'm currently doing some reading on the code and on the related issues to refresh my memory and will come back with a response later today. |
ActualVim will now detect and use this function if available. It works great for me, and is reliably faster than my heuristic method. lunixbochs/ActualVim@8a21e68#diff-b00fc497b0226e8afc26fccf90e2c4b0R300 |
A small update: I'm currently working on a PR that aims to fix #6166/#6159 by doing a ambitious refactoring of normal mode, but still need some days before have something to show. This is something I had planned in 2015 but ended up never finishing, and reading the code on friday/saturday helped me refresh my memory. It is not the simple refactoring I commented about, but I strongly believe it is the best solution for all problems related to async integration with nvim's main loop(it might even allow removing the distinction between immediate/deferred RPC calls in the short term). It is also something that will greatly help in cleaning up some other very old pieces of code. Right now it is hard to estimate how many more days I need to finish this, but I will have another update by the end of the week. |
@tarruda That is great news. I pushed an update to this PR that abuses If that is sound I'm thinking we should merge this and can remove the ugly stuff once the pretty stuff is ready. |
dc3bac5
to
ebfe49d
Compare
src/nvim/api/vim.c
Outdated
char* buf = get_mode(); | ||
bool blocked = input_blocking(); | ||
|
||
ADD(rv, STRING_OBJ(cstr_to_string(buf))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cstr_as_string()
should be used to prevent leaking the buf
string.
It is difficult to tell with certainty. Apparently it is safe, but I think it is irrelevant. I suspect solution implemented here doesn't work. If I understood correctly, you are trying to ensure
Imagine the following chain of events:
What happens next depends on OS races that are out of our control. The two possible scenarios are:
The only way you will get the result you expect( I could be missing something, of course(again, it has been a long time :)), but my theory can be verified by making sure that the requests for Note: Right now |
It can't be irrelevant, because commenting out that line ( Regarding scenario 1:
In that case IIUC |
Confirmed. If |
Maybe because of the logic you added that makes |
Well, yes. And the purpose of that logic is to flush the input queue. If |
True, but why add an API whose purpose is to test if the next call would block, but that has a possibility of returning wrong information? If the user can't rely on the return value 100% of times, he will probably not use it. Even if you still want to add an API that allows the user to sometimes know if the next call would block, I think there's a much simpler way: You can implement a function marked as ASYNC that returns the state of the Here's an example(patch to master): diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index bf11795..fcd20a7 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -127,6 +127,12 @@ Integer nvim_input(String keys)
return (Integer)input_enqueue(keys);
}
+Boolean nvim_can_process_events(void)
+ FUNC_API_ASYNC
+{
+ return input_events_enabled();
+}
+
/// Replaces any terminal codes with the internal representation
///
/// @see replace_termcodes
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 5f0f2ec..d454750 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -165,6 +165,11 @@ void input_disable_events(void)
events_enabled--;
}
+bool input_events_enabled(void)
+{
+ return events_enabled;
+}
+
/// Test whether a file descriptor refers to a terminal.
///
/// @param fd File descriptor. |
I will have a look |
ea8242c
to
ae488e6
Compare
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 neovim#6166; nvim_get_mode() will provoke neovim#6166 if e.g. `d` is operator-pending. Closes neovim#6159
Introduce multiqueue_process_priority() to process only events at or above a certain priority.
It was replaced by the "child queue" concept (MultiQueue).
@lunixbochs I changed the result of nvim_get_mode to be a dictionary:
|
FEATURES: bc4a2e1 help, man.vim: "outline" (TOC) feature neovim#5169 58422f1 'guicursor' works in the TUI (and sends info to UIs) neovim#6423 129f107 api: nvim_get_mode() neovim#6247 0b59f98 api/ui: externalize tabline neovim#6583 bc6d868 'listchars': `Whitespace` highlight group neovim#6367 6afa7d6 writefile() obeys 'fsync' option neovim#6427 c60e409 eval.c refactor (also improves some error messages) neovim#5119 9d200cd getcompletion("cmdline") neovim#6376 2ea7bfc terminal: Support extra arguments in 'shell'. neovim#4504 bf51102 DirChanged autocmd neovim#5928 neovim#6262 1743df8 'cpoptions': "_" flag to toggle `cw` behaviour neovim#6235 22337b1 CTRL-R omits trailing ^M when pasting to cmdline neovim#6137 0e44916 :edit allows unescaped spaces in filename neovim#6119 abdbfd2 eval: Add id() function and make printf("%p") useful neovim#6095 bdfa147 findfile(), :find, gf work in :terminal. neovim#6009 2f38ed1 providers: Disable if `g:loaded_*` exists. b5560a6 setpos() can set lowercase marks in other buffers neovim#5753 7c513d6 Throttle :! output, pulse "..." message. neovim#5396 d2e8c76 v:exiting neovim#5651 :terminal improvements neovim#6185 neovim#6142 - cursor keeps position after leaving insert-mode. - 4ceec30 Follows output only if cursor is at end of buffer. - e7bbd35 new option: 'scrollback' - fedb844 quasi-support for undo and 'modifiable' - b45ddf7 disables 'list' by default - disables 'relativenumber' by default :help now contains full API documentation at `:help api`. man.vim saw numerous improvements. Windows support: - Windows is no longer "experimental", it is fully supported. - Windows package includes a GUI, curl.exe and other utilities. "Vim 8" features: partials, lambdas, packages. FIXES: 12fc1de ops: fix i<c-r> with multi-byte text neovim#6524 dd391bf Windows: system() and friends neovim#6497 13352c0 Windows: os_get_hostname() neovim#6413 16babc6 tui: Less-noisy mouse seqs neovim#6411 3a9dd13 (vim bug) folding edge-cases neovim#6207 f6946c6 job-control: set CLOEXEC on pty processes. neovim#5986 d1afd43 rplugin: Call s:LoadRemotePlugins() on startup. 1215084 backtick-expansion works with `shell=fish` neovim#6224 e32ec03 tui: Improved behavior after resize. neovim#6202 86c2adc edit.c: CTRL-SPC: Insert previously-inserted text. neovim#6090 c318d8e b:changedtick now follows VimL rules neovim#6112 34e24cb terminal: Initialize colors in reverse order neovim#6160 e889917 undo: Don't set b_u_curhead in ex_undojoin() neovim#5869 d25649f undo: :earlier, g-: Set b_u_seq_cur correctly. (neovim#6016) 043d8ba 'Visual-mode put from @. register' neovim#5782 42c922b open_buffer(): Do `BufEnter` for directories. 50d0d89 inccommand: Preview :sub commands only after delimiter neovim#5932 1420e10 CheckHealth improvements neovim#5519 c8d5e92 jobstart(): Return -1 if cmd is not executable. neovim#5671 CHANGES: NVIM_TUI_ENABLE_CURSOR_SHAPE was removed. Use 'guicursor' instead. See https://github.com/neovim/neovim/wiki/Following-HEAD#20170402 81525dc 'mouse=a' is no longer the default. (This will probably change again after it is improved.) neovim#6022 0c1f783 defaults: 'showcmd', 'belloff', 'ruler' neovim#6087 eb0e94f api: {get,set}_option update local options as appropriate neovim#6405 bdcb2a3 "Reading from stdin..." message was removed. neovim#6298
FEATURES: bc4a2e1 help, man.vim: "outline" (TOC) feature neovim#5169 58422f1 'guicursor' works in the TUI (and sends info to UIs) neovim#6423 129f107 api: nvim_get_mode() neovim#6247 0b59f98 api/ui: externalize tabline neovim#6583 bc6d868 'listchars': `Whitespace` highlight group neovim#6367 6afa7d6 writefile() obeys 'fsync' option neovim#6427 c60e409 eval.c refactor (also improves some error messages) neovim#5119 9d200cd getcompletion("cmdline") neovim#6376 2ea7bfc terminal: Support extra arguments in 'shell'. neovim#4504 bf51102 DirChanged autocmd neovim#5928 neovim#6262 1743df8 'cpoptions': "_" flag to toggle `cw` behaviour neovim#6235 22337b1 CTRL-R omits trailing ^M when pasting to cmdline neovim#6137 0e44916 :edit allows unescaped spaces in filename neovim#6119 abdbfd2 eval: Add id() function and make printf("%p") useful neovim#6095 bdfa147 findfile(), :find, gf work in :terminal. neovim#6009 2f38ed1 providers: Disable if `g:loaded_*` exists. b5560a6 setpos() can set lowercase marks in other buffers neovim#5753 7c513d6 Throttle :! output, pulse "..." message. neovim#5396 d2e8c76 v:exiting neovim#5651 :terminal improvements neovim#6185 neovim#6142 - cursor keeps position after leaving insert-mode. - 4ceec30 Follows output only if cursor is at end of buffer. - e7bbd35 new option: 'scrollback' - fedb844 quasi-support for undo and 'modifiable' - b45ddf7 disables 'list' by default - disables 'relativenumber' by default :help now contains full API documentation at `:help api`. man.vim saw numerous improvements. Windows support: - Windows is no longer "experimental", it is fully supported. - Windows package includes a GUI, curl.exe and other utilities. "Vim 8" features: partials, lambdas. SECURITY FIXES: CVE-2017-5953 CVE-2017-6349 CVE-2017-6350 neovim#6485 CHANGES: NVIM_TUI_ENABLE_CURSOR_SHAPE was removed. Use 'guicursor' instead. See https://github.com/neovim/neovim/wiki/Following-HEAD#20170402 81525dc 'mouse=a' is no longer the default. (This will probably change again after it is improved.) neovim#6022 0c1f783 defaults: 'showcmd', 'belloff', 'ruler' neovim#6087 eb0e94f api: {get,set}_option update local options as appropriate neovim#6405 bdcb2a3 "Reading from stdin..." message was removed. neovim#6298 FIXES: 12fc1de ops: fix i<c-r> with multi-byte text neovim#6524 dd391bf Windows: system() and friends neovim#6497 13352c0 Windows: os_get_hostname() neovim#6413 16babc6 tui: Less-noisy mouse seqs neovim#6411 3a9dd13 (vim bug) folding edge-cases neovim#6207 f6946c6 job-control: set CLOEXEC on pty processes. neovim#5986 d1afd43 rplugin: Call s:LoadRemotePlugins() on startup. 1215084 backtick-expansion works with `shell=fish` neovim#6224 e32ec03 tui: Improved behavior after resize. neovim#6202 86c2adc edit.c: CTRL-SPC: Insert previously-inserted text. neovim#6090 c318d8e b:changedtick now follows VimL rules neovim#6112 34e24cb terminal: Initialize colors in reverse order neovim#6160 e889917 undo: Don't set b_u_curhead in ex_undojoin() neovim#5869 d25649f undo: :earlier, g-: Set b_u_seq_cur correctly. (neovim#6016) 043d8ba 'Visual-mode put from @. register' neovim#5782 42c922b open_buffer(): Do `BufEnter` for directories. 50d0d89 inccommand: Preview :sub commands only after delimiter neovim#5932 1420e10 CheckHealth improvements neovim#5519 c8d5e92 jobstart(): Return -1 if cmd is not executable. neovim#5671
FEATURES: bc4a2e1 help, man.vim: "outline" (TOC) feature neovim#5169 58422f1 'guicursor' works in the TUI (and sends info to UIs) neovim#6423 129f107 api: nvim_get_mode() neovim#6247 0b59f98 api/ui: externalize tabline neovim#6583 bc6d868 'listchars': `Whitespace` highlight group neovim#6367 6afa7d6 writefile() obeys 'fsync' option neovim#6427 c60e409 eval.c refactor (also improves some error messages) neovim#5119 9d200cd getcompletion("cmdline") neovim#6376 2ea7bfc terminal: Support extra arguments in 'shell'. neovim#4504 bf51102 DirChanged autocmd neovim#5928 neovim#6262 1743df8 'cpoptions': "_" flag to toggle `cw` behaviour neovim#6235 22337b1 CTRL-R omits trailing ^M when pasting to cmdline neovim#6137 0e44916 :edit allows unescaped spaces in filename neovim#6119 abdbfd2 eval: Add id() function and make printf("%p") useful neovim#6095 bdfa147 findfile(), :find, gf work in :terminal. neovim#6009 2f38ed1 providers: Disable if `g:loaded_*` exists. b5560a6 setpos() can set lowercase marks in other buffers neovim#5753 7c513d6 Throttle :! output, pulse "..." message. neovim#5396 d2e8c76 v:exiting neovim#5651 :terminal improvements neovim#6185 neovim#6142 - cursor keeps position after leaving insert-mode. - 4ceec30 Follows output only if cursor is at end of buffer. - e7bbd35 new option: 'scrollback' - fedb844 quasi-support for undo and 'modifiable' - b45ddf7 disables 'list' by default - disables 'relativenumber' by default :help now contains full API documentation at `:help api`. man.vim saw numerous improvements. Windows support: - Windows is no longer "experimental", it is fully supported. - Windows package includes a GUI, curl.exe and other utilities. "Vim 8" features: partials, lambdas. SECURITY FIXES: CVE-2017-5953 CVE-2017-6349 CVE-2017-6350 neovim#6485 CHANGES: NVIM_TUI_ENABLE_CURSOR_SHAPE was removed. Use 'guicursor' instead. See https://github.com/neovim/neovim/wiki/Following-HEAD#20170402 81525dc 'mouse=a' is no longer the default. (This will probably change again after it is improved.) neovim#6022 0c1f783 defaults: 'showcmd', 'belloff', 'ruler' neovim#6087 eb0e94f api: {get,set}_option update local options as appropriate neovim#6405 bdcb2a3 "Reading from stdin..." message was removed. neovim#6298 FIXES: 12fc1de ops: fix i<c-r> with multi-byte text neovim#6524 dd391bf Windows: system() and friends neovim#6497 13352c0 Windows: os_get_hostname() neovim#6413 16babc6 tui: Less-noisy mouse seqs neovim#6411 3a9dd13 (vim bug) folding edge-cases neovim#6207 f6946c6 job-control: set CLOEXEC on pty processes. neovim#5986 d1afd43 rplugin: Call s:LoadRemotePlugins() on startup. 1215084 backtick-expansion works with `shell=fish` neovim#6224 e32ec03 tui: Improved behavior after resize. neovim#6202 86c2adc edit.c: CTRL-SPC: Insert previously-inserted text. neovim#6090 c318d8e b:changedtick now follows VimL rules neovim#6112 34e24cb terminal: Initialize colors in reverse order neovim#6160 e889917 undo: Don't set b_u_curhead in ex_undojoin() neovim#5869 d25649f undo: :earlier, g-: Set b_u_seq_cur correctly. (neovim#6016) 043d8ba 'Visual-mode put from @. register' neovim#5782 42c922b open_buffer(): Do `BufEnter` for directories. 50d0d89 inccommand: Preview :sub commands only after delimiter neovim#5932 1420e10 CheckHealth improvements neovim#5519 c8d5e92 jobstart(): Return -1 if cmd is not executable. neovim#5671
Until now, asynchronous API functions never flush the input queue.
nvim_get_mode()
is different: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 function.In all cases
nvim_get_mode()
never blocks for more than the time it takes to flush the input/event queue (~µs).Note that this PR doesn't address #6166, and
nvim_get_mode()
will provoke #6166 if e.g.d
is operator-pending. That will be the next thing I look at.Comments from anyone who groks the event loop (@bfredl @tarruda @oni-link) would be very welcome.
Closes #6159
cc @lunixbochs