Skip to content
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

fix(messages)!: vim.ui_attach message callbacks are unsafe #27874

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

luukvbaal
Copy link
Contributor

@luukvbaal luukvbaal commented Mar 15, 2024

Problem: Lua callbacks for "msg_show" events with vim.ui_attach() are
executed when it is not safe. The callback can cause Nvim to
crash, change internal variables which may yet be needed etc.
Solution: Batch "msg_show" events and call Lua callback when it is safe.
Instead of separate events, batch and pass an array of all
messages since the last redraw.

@luukvbaal

This comment was marked as outdated.

@luukvbaal luukvbaal force-pushed the funcerr branch 2 times, most recently from 7900c19 to c9e2259 Compare March 15, 2024 17:34
@zeertzjq
Copy link
Member

Actually ext_messages callback may cause Nvim to crash in many cases, because code calling *msg functions don't expect it to change state.

@luukvbaal
Copy link
Contributor Author

luukvbaal commented Mar 15, 2024

Hmm, does it? The messages are batched and (usually) flushed in normal_redraw() where changing state should be allowed?

But yeah if they are flushed where it isn't allowed I guess that should be prevented.
(P.S. On phone, not actually sure what I wrote is true)

@zeertzjq
Copy link
Member

msg_start() calls msg_ext_ui_flush()

@luukvbaal
Copy link
Contributor Author

luukvbaal commented Mar 15, 2024

Okay, yeah msg_start seems kind of redundant in the situation where ext_messages is used in core and the message grid is removed.

As in, flushing inside msg_start doesn't seem necessary. Perhaps we can insert a special chunk to indicate the start of a new message.

src/nvim/eval/userfunc.c Outdated Show resolved Hide resolved
@zeertzjq
Copy link
Member

The handler may call api functions that reset it, leaving :function active after an error.

Is there at least an interactive example where this happens?

@luukvbaal
Copy link
Contributor Author

luukvbaal commented Mar 15, 2024

Is there at least an interactive example where this happens?

Still on mobile but if you checkout my ext PR and run nvim --clean, you should see the issue when trying to redefine an existing function. I.e.

:func Foo()<CR>
bar<CR>
endf<CR>
:func Foo()<CR>

I think it was try_start() in nvim_buf_set_lines() that unsets did_emsg.

Copy link
Member

@zeertzjq zeertzjq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be solved in API calls instead, because did_emsg and called_emsg are not necessarily equivalent. For example, did_emsg is not incremented with :silent!, while called_emsg is. If ext_messages callback makes the value of did_emsg unreliable then it may also cause other problems like duplicate error messages.

@luukvbaal
Copy link
Contributor Author

Actually ext_messages callback may cause Nvim to crash in many cases, because code calling *msg functions don't expect it to change state.

I think this should be solved in API calls instead

Should we try to avoid both issues at once by not flushing messages where it can lead to these problems?

I.e. instead of having msg_start/end() flush messages, insert some kind of special chunk to separate messages, or stash the events to emit them when it is safe. @bfredl

@luukvbaal luukvbaal marked this pull request as draft March 19, 2024 07:31
@luukvbaal luukvbaal changed the title fix(eval): abort :function on error with ext_messages fix(ext_messages): emitting message events is unsafe Mar 19, 2024
@luukvbaal
Copy link
Contributor Author

luukvbaal commented Mar 19, 2024

The current commit avoids the :function problem this PR originally tried to fix. Also fixes this crash for example:

nvim --clean -S test.lua
-- emit any message

-- test.lua
vim.ui_attach(vim.api.nvim_create_namespace(''), {ext_messages = true}, function(event, ...)
  if event == "msg_show" then
    vim.fn.getchar()
  end
end)

@zeertzjq zeertzjq added the messages UI messages, log messages label Mar 19, 2024
@luukvbaal
Copy link
Contributor Author

luukvbaal commented Mar 19, 2024

Status:

Postponed to 0.11 cycle and considering to change the signature for the vim.ui_attach ext_messages callbacks @folke. Not touching the event itself, since the issue does not apply to remote UIs. Not strictly necessary but vim.ui_attach is still experimental. If we are throttling the message callbacks, it makes more sense to emit a single message event with an array of all the emitted messages, rather than multiple events.

@luukvbaal luukvbaal changed the title fix(ext_messages): emitting message events is unsafe fix(messages): vim.ui_attach() message callbacks are unsafe Mar 19, 2024
@folke
Copy link
Member

folke commented Mar 19, 2024

@luukvbaal makes sense!

I'll fix the Noice side when needed :)

@luukvbaal luukvbaal force-pushed the funcerr branch 7 times, most recently from bb299a0 to e2aa327 Compare March 23, 2024 22:40
@luukvbaal luukvbaal changed the title fix(messages): vim.ui_attach() message callbacks are unsafe fix(messages)!: vim.ui_attach message callbacks are unsafe Mar 23, 2024
Problem:  Lua callbacks for "msg_show" events with vim.ui_attach() are
          executed when it is not safe. The callback can cause Nvim to
          crash, change internal variables which may yet be needed etc.
Solution: Batch "msg_show" events and call Lua callback when it is safe.
          Instead of separate events, batch and pass an array of all
          messages since the last redraw.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
messages UI messages, log messages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants