diff options
| author | Justin M. Keyes <justinkz@gmail.com> | 2024-06-22 12:14:23 +0900 |
|---|---|---|
| committer | Justin M. Keyes <justinkz@gmail.com> | 2026-07-04 23:17:27 +0200 |
| commit | 3b8c19ea460b4abb5b74f91e05a14c0fcf9cc6a6 (patch) | |
| tree | c0417dcda25904ed0dc55757f4026e791833e877 /test/functional/vimscript/executable_spec.lua | |
| parent | 1aced004cef1aa8148f2c6c9d3311298b3b91fc5 (diff) | |
| download | rneovim-3b8c19ea460b4abb5b74f91e05a14c0fcf9cc6a6.tar.gz rneovim-3b8c19ea460b4abb5b74f91e05a14c0fcf9cc6a6.tar.bz2 rneovim-3b8c19ea460b4abb5b74f91e05a14c0fcf9cc6a6.zip | |
fix(bufwrite): cursor flicker on :write
PROBLEM:
Cursor briefly flickers in cmdline when the "written" message is printed
(very noticeable in Neovide with cursor animation).
SOLUTION:
- Mark UI "busy" (cursor hidden) while emitting the message, as done for
the search message (cb2ca54331cd).
- Note: ff68fd6b8a84 moved the message from `filemess()` into
`buf_write..msg_progress`.
- Fix a bug in `tui.c:flush_buf` which manifested after this change. See
ANALYSIS below. https://github.com/libuv/libuv/issues/5182
ANALYSIS:
After this change...
ui_busy_start();
set_keep_msg(msg_progress(IObuff, msg_id, "success", 0, true, true), 0);
ui_busy_stop();
...ASAN analyzer fails on tui_spec.lua test "with non-tty (pipe) stdout/stderr":
2026-07-04T14:09:28.9521023Z = ==32405==ERROR: AddressSanitizer: ABRT on unknown address 0x03e900007e95
...
4 0x7f5506a288fe in abort stdlib/abort.c:79:7
5 0x559afd7113a2 in uv__epoll_ctl_flush
build/src/libuv/src/unix/linux.c:1335:7
6 0x559afd710a81 in uv__io_poll
build/src/libuv/src/unix/linux.c:1448:9
7 0x559afd6f67a7 in uv_run
build/src/libuv/src/unix/core.c:460:5
8 0x559afd2a72cc in flush_buf
nvim/tui/tui.c:2642:5
9 0x559afd2bce9f in tui_flush
nvim/tui/tui.c:1747:3
10 0x559afd2f39a0 in ui_client_event_flush
nvim/auto/ui_events_client.generated.h:64:3
11 0x559afcb8e014 in parse_msgpack
nvim/msgpack_rpc/channel.c:255:11
12 0x559afcb840e7 in receive_msgpack
nvim/msgpack_rpc/channel.c:217:5
13 0x559afc53b2af in read_event
nvim/event/rstream.c:180:23
14 0x559afc53ad52 in invoke_read_cb
nvim/event/rstream.c:233:3
15 0x559afc5382d6 in read_cb
nvim/event/rstream.c:135:3
16 0x559afd707b25 in uv__read
build/src/libuv/src/unix/stream.c:1145:7
17 0x559afd70744a in uv__stream_io
build/src/libuv/src/unix/stream.c:1208:5
18 0x559afd6f727e in uv__io_cb
build/src/libuv/src/unix/core.c:930:5
19 0x559afd710d7a in uv__io_poll
build/src/libuv/src/unix/linux.c:1546:11
20 0x559afd6f67a7 in uv_run
build/src/libuv/src/unix/core.c:460:5
21 0x559afc526e2d in loop_uv_run
nvim/event/loop.c:59:3
22 0x559afc526aa4 in loop_poll_events
nvim/event/loop.c:80:26
23 0x559afd2fdbb7 in ui_client_run
nvim/ui_client.c:172:5
24 0x559afc920d7b in main /home/runner/work/neovim/neovim/src/nvim/main.c:355:5
25 0x7f5506a2a1c9 in __libc_start_call_main
main.h:58:16
26 0x7f5506a2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
27 0x559afbd6d254 in _start
The abort requires two rare conditions:
1. a `uv_write` whose buffer array *ends* in a zero-length buffer, which only
happens by "flush while cursor-hidden".
2. an output fd that *epoll cannot watch*. The only CI test with such an fd is
tui_spec.lua "with non-tty (pipe) stdout/stderr", which runs a TUI with
`stdout > /dev/null`, then runs `:w testF`, which triggers the "written"
message.
Our change guarantees trailing-empty flushes (`:w` fires busy + `ui_flush()`
while busy) in the only test that runs a TUI on `/dev/null`.
0. In a normal (non-busy) no-sync flush, post always contains `cursor_normal`.
1. Our change emits:
```
busy_start → msg_progress("…written") → ui_flush → busy_stop
```
2. TUI flushes *while busy*, so `should_invisible() = true` and `flush_buf`
skips the cursor-restore string (`bufs[2]` is zero-length).
3. libuv *cannot complete* a write whose tail is a zero-length buffer: EVERY
trailing-empty write goes through epoll, even when every byte was already
written. https://github.com/libuv/libuv/issues/5182
4. In `tui_spec.lua:3298`, the output fd is `/dev/null`, whose kernel
`file_operations` has no `.poll` method (`drivers/char/mem.c`), so
`EPOLL_CTL_ADD` fails with `EPERM`.
Trailing empty buffers are semantically pointless, and libuv punishes
them: forced async, one epoll round-trip *per trailing empty*, plus
a 0-byte `write()` syscall.
Helped-by: Shougo Matsushita <Shougo.Matsu@gmail.com>
Helped-by: Fred Sundvik <fsundvik@gmail.com>
Diffstat (limited to 'test/functional/vimscript/executable_spec.lua')
0 files changed, 0 insertions, 0 deletions