mirror of
https://github.com/neovim/neovim.git
synced 2025-12-11 17:12:40 +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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool clear_w_buf = false;
|
||||||
if (win != NULL // Avoid bogus clang warning.
|
if (win != NULL // Avoid bogus clang warning.
|
||||||
&& win_valid_any_tab(win)
|
&& win_valid_any_tab(win)
|
||||||
&& win->w_buffer == buf) {
|
&& 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.
|
// 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.
|
// Remove the buffer from the list.
|
||||||
if (wipe_buf) {
|
if (wipe_buf) {
|
||||||
|
if (clear_w_buf) {
|
||||||
|
win->w_buffer = NULL;
|
||||||
|
}
|
||||||
// Do not wipe out the buffer if it is used in a window.
|
// Do not wipe out the buffer if it is used in a window.
|
||||||
if (buf->b_nwindows > 0) {
|
if (buf->b_nwindows > 0) {
|
||||||
return false;
|
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->b_p_initialized = false;
|
||||||
}
|
}
|
||||||
buf_clear_file(buf);
|
buf_clear_file(buf);
|
||||||
|
if (clear_w_buf) {
|
||||||
|
win->w_buffer = NULL;
|
||||||
|
}
|
||||||
if (del_buf) {
|
if (del_buf) {
|
||||||
buf->b_p_bl = false;
|
buf->b_p_bl = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -524,3 +524,50 @@ describe('Vimscript dictionary notifications', function()
|
|||||||
eq({ 'W1', 'W2', 'W2', 'W1' }, eval('g:calls'))
|
eq({ 'W1', 'W2', 'W2', 'W1' }, eval('g:calls'))
|
||||||
end)
|
end)
|
||||||
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