aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Hahler <git@thequod.de>2019-10-09 19:40:50 +0200
committerJan Edmund Lazo <jan.lazo@mail.utoronto.ca>2021-03-30 21:55:50 -0400
commit4c76b1e981f072229944a22e5d5ee76fe42d994a (patch)
tree1db14e1ff707894bb8568d83efc0b77ba492d11e
parentc20ae3aadbcc1210faad4cd3cf6a8444f198b19d (diff)
downloadrneovim-4c76b1e981f072229944a22e5d5ee76fe42d994a.tar.gz
rneovim-4c76b1e981f072229944a22e5d5ee76fe42d994a.tar.bz2
rneovim-4c76b1e981f072229944a22e5d5ee76fe42d994a.zip
Test and initial fix for crash with dictwatcherdel
Fixes https://github.com/neovim/neovim/issues/11188.
-rw-r--r--src/nvim/eval/typval.c10
-rw-r--r--src/nvim/eval/typval.h1
-rw-r--r--test/functional/ex_cmds/dict_notifications_spec.lua20
3 files changed, 30 insertions, 1 deletions
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 9be487f4fd..f8b48d0139 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1109,6 +1109,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern,
watcher->key_pattern_len = key_pattern_len;
watcher->callback = callback;
watcher->busy = false;
+ watcher->needs_free = false;
QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node);
}
@@ -1197,7 +1198,11 @@ bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern,
}
QUEUE_REMOVE(w);
- tv_dict_watcher_free(watcher);
+ if (watcher->busy) {
+ watcher->needs_free = true;
+ } else {
+ tv_dict_watcher_free(watcher);
+ }
return true;
}
@@ -1268,6 +1273,9 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key,
callback_call(&watcher->callback, 3, argv, &rettv);
watcher->busy = false;
tv_clear(&rettv);
+ if (watcher->needs_free) {
+ tv_dict_watcher_free(watcher);
+ }
}
}
tv_dict_unref(dict);
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 531b17cb59..2b4612016b 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -89,6 +89,7 @@ typedef struct dict_watcher {
size_t key_pattern_len;
QUEUE node;
bool busy; // prevent recursion if the dict is changed in the callback
+ bool needs_free;
} DictWatcher;
/// Bool variable values
diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua
index 5c67431221..3e6c248411 100644
--- a/test/functional/ex_cmds/dict_notifications_spec.lua
+++ b/test/functional/ex_cmds/dict_notifications_spec.lua
@@ -371,4 +371,24 @@ describe('VimL dictionary notifications', function()
eq(1, eval('g:called'))
end)
+ it('does not crash when using dictwatcherdel in callback', function()
+ source([[
+ let g:d = {}
+
+ function! W(...)
+ call dictwatcherdel(g:d, '*', function('W'))
+ try
+ call dictwatcherdel({}, 'meh', function('tr'))
+ catch
+ let g:exc = v:exception
+ endtry
+ endfunction
+
+ call dictwatcheradd(g:d, '*', function('W'))
+ let g:d.foo = 23
+ ]])
+ eq(23, eval('g:d.foo'))
+ eq("Vim(call):Couldn't find a watcher matching key and callback", eval('g:exc'))
+ end)
+
end)