diff options
author | Daniel Hahler <git@thequod.de> | 2019-10-09 19:40:50 +0200 |
---|---|---|
committer | Jan Edmund Lazo <jan.lazo@mail.utoronto.ca> | 2021-03-30 21:55:50 -0400 |
commit | 4c76b1e981f072229944a22e5d5ee76fe42d994a (patch) | |
tree | 1db14e1ff707894bb8568d83efc0b77ba492d11e | |
parent | c20ae3aadbcc1210faad4cd3cf6a8444f198b19d (diff) | |
download | rneovim-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.c | 10 | ||||
-rw-r--r-- | src/nvim/eval/typval.h | 1 | ||||
-rw-r--r-- | test/functional/ex_cmds/dict_notifications_spec.lua | 20 |
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) |