mirror of
https://github.com/neovim/neovim.git
synced 2025-12-09 08:02:38 +00:00
fix(buffer): defer w_buffer clearing to prevent dict watcher crash #36748
(cherry picked from commit f9ef1a4cab)
This commit is contained in:
committed by
github-actions[bot]
parent
9fb49aacde
commit
fa24e045e9
@@ -679,10 +679,14 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
||||
return false;
|
||||
}
|
||||
|
||||
bool clear_w_buf = false;
|
||||
if (win != NULL // Avoid bogus clang warning.
|
||||
&& win_valid_any_tab(win)
|
||||
&& win->w_buffer == buf) {
|
||||
win->w_buffer = NULL; // make sure we don't use the buffer now
|
||||
// Defer clearing w_buffer until after operations that may invoke dict
|
||||
// watchers (e.g., buf_clear_file()), so callers like tabpagebuflist()
|
||||
// never see a window in the winlist with a NULL buffer.
|
||||
clear_w_buf = true;
|
||||
}
|
||||
|
||||
// Autocommands may have opened or closed windows for this buffer.
|
||||
@@ -693,6 +697,9 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
||||
|
||||
// Remove the buffer from the list.
|
||||
if (wipe_buf) {
|
||||
if (clear_w_buf) {
|
||||
win->w_buffer = NULL;
|
||||
}
|
||||
// Do not wipe out the buffer if it is used in a window.
|
||||
if (buf->b_nwindows > 0) {
|
||||
return false;
|
||||
@@ -730,6 +737,9 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
||||
buf->b_p_initialized = false;
|
||||
}
|
||||
buf_clear_file(buf);
|
||||
if (clear_w_buf) {
|
||||
win->w_buffer = NULL;
|
||||
}
|
||||
if (del_buf) {
|
||||
buf->b_p_bl = false;
|
||||
}
|
||||
|
||||
@@ -524,3 +524,50 @@ describe('Vimscript dictionary notifications', function()
|
||||
eq({ 'W1', 'W2', 'W2', 'W1' }, eval('g:calls'))
|
||||
end)
|
||||
end)
|
||||
describe('tabpagebuflist() with dict watcher during buffer close/wipe', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
end)
|
||||
|
||||
it(
|
||||
'does not segfault when called from dict watcher on b:changedtick (bufhidden=unload)',
|
||||
function()
|
||||
command([[
|
||||
new
|
||||
set bufhidden=unload
|
||||
call dictwatcheradd(b:, 'changedtick', {-> tabpagebuflist()})
|
||||
close
|
||||
]])
|
||||
|
||||
assert_alive()
|
||||
end
|
||||
)
|
||||
|
||||
it('does not segfault when wiping buffer with dict watcher', function()
|
||||
command([[
|
||||
new
|
||||
call setline(1, 'test')
|
||||
call dictwatcheradd(b:, 'changedtick', {-> tabpagebuflist()})
|
||||
bwipeout!
|
||||
]])
|
||||
|
||||
assert_alive()
|
||||
end)
|
||||
|
||||
it('does not segfault with multiple windows in the tabpage', function()
|
||||
command([[
|
||||
" create two windows in the current tab
|
||||
edit foo
|
||||
vnew
|
||||
call setline(1, 'bar')
|
||||
|
||||
" attach watcher to the current buffer in the split
|
||||
call dictwatcheradd(b:, 'changedtick', {-> tabpagebuflist()})
|
||||
|
||||
" close the split window (triggers close_buffer on this buffer)
|
||||
close
|
||||
]])
|
||||
|
||||
assert_alive()
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user