diff options
-rw-r--r-- | src/nvim/eval.c | 17 | ||||
-rw-r--r-- | test/functional/eval/timer_spec.lua | 15 |
2 files changed, 29 insertions, 3 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7839a7f645..38aa587d37 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -435,6 +435,7 @@ typedef struct { TimeWatcher tw; int timer_id; int repeat_count; + int refcount; long timeout; bool stopped; ufunc_T *callback; @@ -16775,6 +16776,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv) func->uf_refcount++; timer = xmalloc(sizeof *timer); + timer->refcount = 1; timer->stopped = false; timer->repeat_count = repeat; timer->timeout = timeout; @@ -16817,6 +16819,7 @@ static void timer_due_cb(TimeWatcher *tw, void *data) if (timer->stopped) { return; } + timer->refcount++; // if repeat was negative repeat forever if (timer->repeat_count >= 0 && --timer->repeat_count == 0) { timer_stop(timer); @@ -16840,6 +16843,7 @@ static void timer_due_cb(TimeWatcher *tw, void *data) // when the main loop is blocked. time_watcher_start(&timer->tw, timer_due_cb, 0, 0); } + timer_decref(timer); } static void timer_stop(timer_T *timer) @@ -16850,17 +16854,24 @@ static void timer_stop(timer_T *timer) } timer->stopped = true; time_watcher_stop(&timer->tw); - time_watcher_close(&timer->tw, timer_free_cb); + time_watcher_close(&timer->tw, timer_close_cb); } // invoked on next event loop tick, so queue is empty -static void timer_free_cb(TimeWatcher *tw, void *data) +static void timer_close_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; queue_free(timer->tw.events); user_func_unref(timer->callback); pmap_del(uint64_t)(timers, timer->timer_id); - xfree(timer); + timer_decref(timer); +} + +static void timer_decref(timer_T *timer) +{ + if (--timer->refcount == 0) { + xfree(timer); + } } void timer_teardown(void) diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua index 2f83edb9e4..80c16e6ee3 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/eval/timer_spec.lua @@ -119,6 +119,21 @@ describe('timers', function() eq(2,eval("g:val2")) end) + it('do not crash when processing events in the handler', function() + source([[ + let g:val = 0 + func! MyHandler(timer) + call timer_stop(a:timer) + sleep 100m + let g:val += 1 + endfunc + ]]) + execute("call timer_start(5, 'MyHandler', {'repeat': 1})") + run(nil, nil, nil, 300) + eq(1,eval("g:val")) + end) + + it("doesn't mess up the cmdline", function() local screen = Screen.new(40, 6) screen:attach() |