forked from aniani/vim
patch 8.2.0256: time and timer related code is spread out
Problem: Time and timer related code is spread out. Solution: Move time and timer related code to a new file. (Yegappan Lakshmanan, closes #5604)
This commit is contained in:
492
src/ex_cmds2.c
492
src/ex_cmds2.c
@@ -14,498 +14,6 @@
|
||||
#include "vim.h"
|
||||
#include "version.h"
|
||||
|
||||
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||
# if defined(FEAT_TIMERS) || defined(PROTO)
|
||||
static timer_T *first_timer = NULL;
|
||||
static long last_timer_id = 0;
|
||||
|
||||
/*
|
||||
* Return time left until "due". Negative if past "due".
|
||||
*/
|
||||
long
|
||||
proftime_time_left(proftime_T *due, proftime_T *now)
|
||||
{
|
||||
# ifdef MSWIN
|
||||
LARGE_INTEGER fr;
|
||||
|
||||
if (now->QuadPart > due->QuadPart)
|
||||
return 0;
|
||||
QueryPerformanceFrequency(&fr);
|
||||
return (long)(((double)(due->QuadPart - now->QuadPart)
|
||||
/ (double)fr.QuadPart) * 1000);
|
||||
# else
|
||||
if (now->tv_sec > due->tv_sec)
|
||||
return 0;
|
||||
return (due->tv_sec - now->tv_sec) * 1000
|
||||
+ (due->tv_usec - now->tv_usec) / 1000;
|
||||
# endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a timer in the list of timers.
|
||||
*/
|
||||
static void
|
||||
insert_timer(timer_T *timer)
|
||||
{
|
||||
timer->tr_next = first_timer;
|
||||
timer->tr_prev = NULL;
|
||||
if (first_timer != NULL)
|
||||
first_timer->tr_prev = timer;
|
||||
first_timer = timer;
|
||||
did_add_timer = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a timer out of the list of timers.
|
||||
*/
|
||||
static void
|
||||
remove_timer(timer_T *timer)
|
||||
{
|
||||
if (timer->tr_prev == NULL)
|
||||
first_timer = timer->tr_next;
|
||||
else
|
||||
timer->tr_prev->tr_next = timer->tr_next;
|
||||
if (timer->tr_next != NULL)
|
||||
timer->tr_next->tr_prev = timer->tr_prev;
|
||||
}
|
||||
|
||||
static void
|
||||
free_timer(timer_T *timer)
|
||||
{
|
||||
free_callback(&timer->tr_callback);
|
||||
vim_free(timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a timer and return it. NULL if out of memory.
|
||||
* Caller should set the callback.
|
||||
*/
|
||||
timer_T *
|
||||
create_timer(long msec, int repeat)
|
||||
{
|
||||
timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
|
||||
long prev_id = last_timer_id;
|
||||
|
||||
if (timer == NULL)
|
||||
return NULL;
|
||||
if (++last_timer_id <= prev_id)
|
||||
// Overflow! Might cause duplicates...
|
||||
last_timer_id = 0;
|
||||
timer->tr_id = last_timer_id;
|
||||
insert_timer(timer);
|
||||
if (repeat != 0)
|
||||
timer->tr_repeat = repeat - 1;
|
||||
timer->tr_interval = msec;
|
||||
|
||||
profile_setlimit(msec, &timer->tr_due);
|
||||
return timer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invoke the callback of "timer".
|
||||
*/
|
||||
static void
|
||||
timer_callback(timer_T *timer)
|
||||
{
|
||||
typval_T rettv;
|
||||
typval_T argv[2];
|
||||
|
||||
argv[0].v_type = VAR_NUMBER;
|
||||
argv[0].vval.v_number = (varnumber_T)timer->tr_id;
|
||||
argv[1].v_type = VAR_UNKNOWN;
|
||||
|
||||
call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
|
||||
clear_tv(&rettv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call timers that are due.
|
||||
* Return the time in msec until the next timer is due.
|
||||
* Returns -1 if there are no pending timers.
|
||||
*/
|
||||
long
|
||||
check_due_timer(void)
|
||||
{
|
||||
timer_T *timer;
|
||||
timer_T *timer_next;
|
||||
long this_due;
|
||||
long next_due = -1;
|
||||
proftime_T now;
|
||||
int did_one = FALSE;
|
||||
int need_update_screen = FALSE;
|
||||
long current_id = last_timer_id;
|
||||
|
||||
// Don't run any timers while exiting or dealing with an error.
|
||||
if (exiting || aborting())
|
||||
return next_due;
|
||||
|
||||
profile_start(&now);
|
||||
for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
|
||||
{
|
||||
timer_next = timer->tr_next;
|
||||
|
||||
if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
|
||||
continue;
|
||||
this_due = proftime_time_left(&timer->tr_due, &now);
|
||||
if (this_due <= 1)
|
||||
{
|
||||
// Save and restore a lot of flags, because the timer fires while
|
||||
// waiting for a character, which might be halfway a command.
|
||||
int save_timer_busy = timer_busy;
|
||||
int save_vgetc_busy = vgetc_busy;
|
||||
int save_did_emsg = did_emsg;
|
||||
int save_called_emsg = called_emsg;
|
||||
int save_must_redraw = must_redraw;
|
||||
int save_trylevel = trylevel;
|
||||
int save_did_throw = did_throw;
|
||||
int save_ex_pressedreturn = get_pressedreturn();
|
||||
int save_may_garbage_collect = may_garbage_collect;
|
||||
except_T *save_current_exception = current_exception;
|
||||
vimvars_save_T vvsave;
|
||||
|
||||
// Create a scope for running the timer callback, ignoring most of
|
||||
// the current scope, such as being inside a try/catch.
|
||||
timer_busy = timer_busy > 0 || vgetc_busy > 0;
|
||||
vgetc_busy = 0;
|
||||
called_emsg = 0;
|
||||
did_emsg = FALSE;
|
||||
did_uncaught_emsg = FALSE;
|
||||
must_redraw = 0;
|
||||
trylevel = 0;
|
||||
did_throw = FALSE;
|
||||
current_exception = NULL;
|
||||
may_garbage_collect = FALSE;
|
||||
save_vimvars(&vvsave);
|
||||
|
||||
timer->tr_firing = TRUE;
|
||||
timer_callback(timer);
|
||||
timer->tr_firing = FALSE;
|
||||
|
||||
timer_next = timer->tr_next;
|
||||
did_one = TRUE;
|
||||
timer_busy = save_timer_busy;
|
||||
vgetc_busy = save_vgetc_busy;
|
||||
if (did_uncaught_emsg)
|
||||
++timer->tr_emsg_count;
|
||||
did_emsg = save_did_emsg;
|
||||
called_emsg = save_called_emsg;
|
||||
trylevel = save_trylevel;
|
||||
did_throw = save_did_throw;
|
||||
current_exception = save_current_exception;
|
||||
restore_vimvars(&vvsave);
|
||||
if (must_redraw != 0)
|
||||
need_update_screen = TRUE;
|
||||
must_redraw = must_redraw > save_must_redraw
|
||||
? must_redraw : save_must_redraw;
|
||||
set_pressedreturn(save_ex_pressedreturn);
|
||||
may_garbage_collect = save_may_garbage_collect;
|
||||
|
||||
// Only fire the timer again if it repeats and stop_timer() wasn't
|
||||
// called while inside the callback (tr_id == -1).
|
||||
if (timer->tr_repeat != 0 && timer->tr_id != -1
|
||||
&& timer->tr_emsg_count < 3)
|
||||
{
|
||||
profile_setlimit(timer->tr_interval, &timer->tr_due);
|
||||
this_due = proftime_time_left(&timer->tr_due, &now);
|
||||
if (this_due < 1)
|
||||
this_due = 1;
|
||||
if (timer->tr_repeat > 0)
|
||||
--timer->tr_repeat;
|
||||
}
|
||||
else
|
||||
{
|
||||
this_due = -1;
|
||||
remove_timer(timer);
|
||||
free_timer(timer);
|
||||
}
|
||||
}
|
||||
if (this_due > 0 && (next_due == -1 || next_due > this_due))
|
||||
next_due = this_due;
|
||||
}
|
||||
|
||||
if (did_one)
|
||||
redraw_after_callback(need_update_screen);
|
||||
|
||||
#ifdef FEAT_BEVAL_TERM
|
||||
if (bevalexpr_due_set)
|
||||
{
|
||||
this_due = proftime_time_left(&bevalexpr_due, &now);
|
||||
if (this_due <= 1)
|
||||
{
|
||||
bevalexpr_due_set = FALSE;
|
||||
if (balloonEval == NULL)
|
||||
{
|
||||
balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
|
||||
balloonEvalForTerm = TRUE;
|
||||
}
|
||||
if (balloonEval != NULL)
|
||||
{
|
||||
general_beval_cb(balloonEval, 0);
|
||||
setcursor();
|
||||
out_flush();
|
||||
}
|
||||
}
|
||||
else if (next_due == -1 || next_due > this_due)
|
||||
next_due = this_due;
|
||||
}
|
||||
#endif
|
||||
#ifdef FEAT_TERMINAL
|
||||
// Some terminal windows may need their buffer updated.
|
||||
next_due = term_check_timers(next_due, &now);
|
||||
#endif
|
||||
|
||||
return current_id != last_timer_id ? 1 : next_due;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a timer by ID. Returns NULL if not found;
|
||||
*/
|
||||
static timer_T *
|
||||
find_timer(long id)
|
||||
{
|
||||
timer_T *timer;
|
||||
|
||||
if (id >= 0)
|
||||
{
|
||||
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
|
||||
if (timer->tr_id == id)
|
||||
return timer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Stop a timer and delete it.
|
||||
*/
|
||||
void
|
||||
stop_timer(timer_T *timer)
|
||||
{
|
||||
if (timer->tr_firing)
|
||||
// Free the timer after the callback returns.
|
||||
timer->tr_id = -1;
|
||||
else
|
||||
{
|
||||
remove_timer(timer);
|
||||
free_timer(timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stop_all_timers(void)
|
||||
{
|
||||
timer_T *timer;
|
||||
timer_T *timer_next;
|
||||
|
||||
for (timer = first_timer; timer != NULL; timer = timer_next)
|
||||
{
|
||||
timer_next = timer->tr_next;
|
||||
stop_timer(timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_timer_info(typval_T *rettv, timer_T *timer)
|
||||
{
|
||||
list_T *list = rettv->vval.v_list;
|
||||
dict_T *dict = dict_alloc();
|
||||
dictitem_T *di;
|
||||
long remaining;
|
||||
proftime_T now;
|
||||
|
||||
if (dict == NULL)
|
||||
return;
|
||||
list_append_dict(list, dict);
|
||||
|
||||
dict_add_number(dict, "id", timer->tr_id);
|
||||
dict_add_number(dict, "time", (long)timer->tr_interval);
|
||||
|
||||
profile_start(&now);
|
||||
remaining = proftime_time_left(&timer->tr_due, &now);
|
||||
dict_add_number(dict, "remaining", (long)remaining);
|
||||
|
||||
dict_add_number(dict, "repeat",
|
||||
(long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1));
|
||||
dict_add_number(dict, "paused", (long)(timer->tr_paused));
|
||||
|
||||
di = dictitem_alloc((char_u *)"callback");
|
||||
if (di != NULL)
|
||||
{
|
||||
if (dict_add(dict, di) == FAIL)
|
||||
vim_free(di);
|
||||
else
|
||||
put_callback(&timer->tr_callback, &di->di_tv);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_timer_info_all(typval_T *rettv)
|
||||
{
|
||||
timer_T *timer;
|
||||
|
||||
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
|
||||
if (timer->tr_id != -1)
|
||||
add_timer_info(rettv, timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark references in partials of timers.
|
||||
*/
|
||||
int
|
||||
set_ref_in_timer(int copyID)
|
||||
{
|
||||
int abort = FALSE;
|
||||
timer_T *timer;
|
||||
typval_T tv;
|
||||
|
||||
for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
|
||||
{
|
||||
if (timer->tr_callback.cb_partial != NULL)
|
||||
{
|
||||
tv.v_type = VAR_PARTIAL;
|
||||
tv.vval.v_partial = timer->tr_callback.cb_partial;
|
||||
}
|
||||
else
|
||||
{
|
||||
tv.v_type = VAR_FUNC;
|
||||
tv.vval.v_string = timer->tr_callback.cb_name;
|
||||
}
|
||||
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
|
||||
}
|
||||
return abort;
|
||||
}
|
||||
|
||||
# if defined(EXITFREE) || defined(PROTO)
|
||||
void
|
||||
timer_free_all()
|
||||
{
|
||||
timer_T *timer;
|
||||
|
||||
while (first_timer != NULL)
|
||||
{
|
||||
timer = first_timer;
|
||||
remove_timer(timer);
|
||||
free_timer(timer);
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
/*
|
||||
* "timer_info([timer])" function
|
||||
*/
|
||||
void
|
||||
f_timer_info(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
timer_T *timer = NULL;
|
||||
|
||||
if (rettv_list_alloc(rettv) != OK)
|
||||
return;
|
||||
if (argvars[0].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
if (argvars[0].v_type != VAR_NUMBER)
|
||||
emsg(_(e_number_exp));
|
||||
else
|
||||
{
|
||||
timer = find_timer((int)tv_get_number(&argvars[0]));
|
||||
if (timer != NULL)
|
||||
add_timer_info(rettv, timer);
|
||||
}
|
||||
}
|
||||
else
|
||||
add_timer_info_all(rettv);
|
||||
}
|
||||
|
||||
/*
|
||||
* "timer_pause(timer, paused)" function
|
||||
*/
|
||||
void
|
||||
f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
{
|
||||
timer_T *timer = NULL;
|
||||
int paused = (int)tv_get_number(&argvars[1]);
|
||||
|
||||
if (argvars[0].v_type != VAR_NUMBER)
|
||||
emsg(_(e_number_exp));
|
||||
else
|
||||
{
|
||||
timer = find_timer((int)tv_get_number(&argvars[0]));
|
||||
if (timer != NULL)
|
||||
timer->tr_paused = paused;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "timer_start(time, callback [, options])" function
|
||||
*/
|
||||
void
|
||||
f_timer_start(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
long msec = (long)tv_get_number(&argvars[0]);
|
||||
timer_T *timer;
|
||||
int repeat = 0;
|
||||
callback_T callback;
|
||||
dict_T *dict;
|
||||
|
||||
rettv->vval.v_number = -1;
|
||||
if (check_secure())
|
||||
return;
|
||||
if (argvars[2].v_type != VAR_UNKNOWN)
|
||||
{
|
||||
if (argvars[2].v_type != VAR_DICT
|
||||
|| (dict = argvars[2].vval.v_dict) == NULL)
|
||||
{
|
||||
semsg(_(e_invarg2), tv_get_string(&argvars[2]));
|
||||
return;
|
||||
}
|
||||
if (dict_find(dict, (char_u *)"repeat", -1) != NULL)
|
||||
repeat = dict_get_number(dict, (char_u *)"repeat");
|
||||
}
|
||||
|
||||
callback = get_callback(&argvars[1]);
|
||||
if (callback.cb_name == NULL)
|
||||
return;
|
||||
|
||||
timer = create_timer(msec, repeat);
|
||||
if (timer == NULL)
|
||||
free_callback(&callback);
|
||||
else
|
||||
{
|
||||
set_callback(&timer->tr_callback, &callback);
|
||||
rettv->vval.v_number = (varnumber_T)timer->tr_id;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "timer_stop(timer)" function
|
||||
*/
|
||||
void
|
||||
f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
{
|
||||
timer_T *timer;
|
||||
|
||||
if (argvars[0].v_type != VAR_NUMBER)
|
||||
{
|
||||
emsg(_(e_number_exp));
|
||||
return;
|
||||
}
|
||||
timer = find_timer((int)tv_get_number(&argvars[0]));
|
||||
if (timer != NULL)
|
||||
stop_timer(timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* "timer_stopall()" function
|
||||
*/
|
||||
void
|
||||
f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
{
|
||||
stop_all_timers();
|
||||
}
|
||||
|
||||
# endif // FEAT_TIMERS
|
||||
|
||||
#endif // FEAT_EVAL
|
||||
|
||||
/*
|
||||
* If 'autowrite' option set, try to write the file.
|
||||
* Careful: autocommands may make "buf" invalid!
|
||||
|
Reference in New Issue
Block a user