fix(lua): disallow vim.wait() in fast contexts

`vim.wait()` cannot be called in a fast callback since the main loop
cannot be run in that context as it is not reentrant

Fixes #26122
This commit is contained in:
Lewis Russell 2023-11-21 11:24:30 +00:00 committed by Lewis Russell
parent 6343d41436
commit 84bbe4b0ca
6 changed files with 29 additions and 8 deletions

View File

@ -1143,6 +1143,8 @@ vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
milliseconds (default 200). Nvim still processes other events during this
time.
Cannot be called while in an |api-fast| event.
Examples: >lua
---
-- Wait for 100 ms, allowing other events to process
@ -1173,8 +1175,7 @@ vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()*
• {interval} (integer|nil) (Approximate) number of milliseconds to
wait between polls
• {fast_only} (boolean|nil) If true, only |api-fast| events will be
processed. If called from while in an |api-fast| event,
will automatically be set to `true`.
processed.
Return: ~
boolean, nil|-1|-2
@ -1828,7 +1829,8 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()*
• pid (integer) Process ID
• wait (fun(timeout: integer|nil): SystemCompleted) Wait for the
process to complete. Upon timeout the process is sent the KILL
signal (9) and the exit code is set to 124.
signal (9) and the exit code is set to 124. Cannot be called in
|api-fast|.
• SystemCompleted is an object with the fields:
• code: (integer)
• signal: (integer)

View File

@ -325,6 +325,8 @@ The following changes to existing APIs or features add new behavior.
NOTE: the regexp engine still has a hard-coded limit of considering
6 composing chars only.
• |vim.wait()| is no longer allowed to be called in |api-fast|.
==============================================================================
REMOVED FEATURES *news-removed*

View File

@ -124,7 +124,8 @@ vim.log = {
--- @return vim.SystemObj Object with the fields:
--- - pid (integer) Process ID
--- - wait (fun(timeout: integer|nil): SystemCompleted) Wait for the process to complete. Upon
--- timeout the process is sent the KILL signal (9) and the exit code is set to 124.
--- timeout the process is sent the KILL signal (9) and the exit code is set to 124. Cannot
--- be called in |api-fast|.
--- - SystemCompleted is an object with the fields:
--- - code: (integer)
--- - signal: (integer)

View File

@ -205,6 +205,8 @@ function vim.schedule(fn) end
--- milliseconds (default 200). Nvim still processes other events during
--- this time.
---
--- Cannot be called while in an |api-fast| event.
---
--- Examples:
---
--- ```lua
@ -235,8 +237,6 @@ function vim.schedule(fn) end
--- @param callback? fun(): boolean Optional callback. Waits until {callback} returns true
--- @param interval? integer (Approximate) number of milliseconds to wait between polls
--- @param fast_only? boolean If true, only |api-fast| events will be processed.
--- If called from while in an |api-fast| event, will
--- automatically be set to `true`.
--- @return boolean, nil|-1|-2
--- - If {callback} returns `true` during the {time}: `true, nil`
--- - If {callback} never returns `true` during the {time}: `false, -1`

View File

@ -411,6 +411,10 @@ static bool nlua_wait_condition(lua_State *lstate, int *status, bool *callback_r
static int nlua_wait(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
if (in_fast_callback) {
return luaL_error(lstate, e_luv_api_disabled, "vim.wait");
}
intptr_t timeout = luaL_checkinteger(lstate, 1);
if (timeout < 0) {
return luaL_error(lstate, "timeout must be >= 0");
@ -449,8 +453,7 @@ static int nlua_wait(lua_State *lstate)
fast_only = lua_toboolean(lstate, 4);
}
MultiQueue *loop_events = fast_only || in_fast_callback > 0
? main_loop.fast_events : main_loop.events;
MultiQueue *loop_events = fast_only ? main_loop.fast_events : main_loop.events;
TimeWatcher *tw = xmalloc(sizeof(TimeWatcher));

View File

@ -2769,6 +2769,19 @@ describe('lua stdlib', function()
eq({'notification', 'wait', {-2}}, next_msg(500))
end)
end)
it('should not run in fast callbacks #26122', function()
exec_lua([[
vim.uv.new_timer():start(0, 100, function()
local count = 0
vim.wait(100, function()
count = count + 1
return count == 10
end, 100)
end)
]])
assert_alive()
end)
end)
it('vim.notify_once', function()