comparison src/ex_cmds2.c @ 19396:a961efb326e5 v8.2.0256

patch 8.2.0256: time and timer related code is spread out Commit: https://github.com/vim/vim/commit/0a8fed6231c84e4e1b3a7dd6c0d95d3f98207fe0 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Feb 14 13:22:17 2020 +0100 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)
author Bram Moolenaar <Bram@vim.org>
date Fri, 14 Feb 2020 13:30:05 +0100
parents 5c405689da3e
children 435726a03481
comparison
equal deleted inserted replaced
19395:eb5ef6f5f58b 19396:a961efb326e5
11 * ex_cmds2.c: some more functions for command line commands 11 * ex_cmds2.c: some more functions for command line commands
12 */ 12 */
13 13
14 #include "vim.h" 14 #include "vim.h"
15 #include "version.h" 15 #include "version.h"
16
17 #if defined(FEAT_EVAL) || defined(PROTO)
18 # if defined(FEAT_TIMERS) || defined(PROTO)
19 static timer_T *first_timer = NULL;
20 static long last_timer_id = 0;
21
22 /*
23 * Return time left until "due". Negative if past "due".
24 */
25 long
26 proftime_time_left(proftime_T *due, proftime_T *now)
27 {
28 # ifdef MSWIN
29 LARGE_INTEGER fr;
30
31 if (now->QuadPart > due->QuadPart)
32 return 0;
33 QueryPerformanceFrequency(&fr);
34 return (long)(((double)(due->QuadPart - now->QuadPart)
35 / (double)fr.QuadPart) * 1000);
36 # else
37 if (now->tv_sec > due->tv_sec)
38 return 0;
39 return (due->tv_sec - now->tv_sec) * 1000
40 + (due->tv_usec - now->tv_usec) / 1000;
41 # endif
42 }
43
44 /*
45 * Insert a timer in the list of timers.
46 */
47 static void
48 insert_timer(timer_T *timer)
49 {
50 timer->tr_next = first_timer;
51 timer->tr_prev = NULL;
52 if (first_timer != NULL)
53 first_timer->tr_prev = timer;
54 first_timer = timer;
55 did_add_timer = TRUE;
56 }
57
58 /*
59 * Take a timer out of the list of timers.
60 */
61 static void
62 remove_timer(timer_T *timer)
63 {
64 if (timer->tr_prev == NULL)
65 first_timer = timer->tr_next;
66 else
67 timer->tr_prev->tr_next = timer->tr_next;
68 if (timer->tr_next != NULL)
69 timer->tr_next->tr_prev = timer->tr_prev;
70 }
71
72 static void
73 free_timer(timer_T *timer)
74 {
75 free_callback(&timer->tr_callback);
76 vim_free(timer);
77 }
78
79 /*
80 * Create a timer and return it. NULL if out of memory.
81 * Caller should set the callback.
82 */
83 timer_T *
84 create_timer(long msec, int repeat)
85 {
86 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
87 long prev_id = last_timer_id;
88
89 if (timer == NULL)
90 return NULL;
91 if (++last_timer_id <= prev_id)
92 // Overflow! Might cause duplicates...
93 last_timer_id = 0;
94 timer->tr_id = last_timer_id;
95 insert_timer(timer);
96 if (repeat != 0)
97 timer->tr_repeat = repeat - 1;
98 timer->tr_interval = msec;
99
100 profile_setlimit(msec, &timer->tr_due);
101 return timer;
102 }
103
104 /*
105 * Invoke the callback of "timer".
106 */
107 static void
108 timer_callback(timer_T *timer)
109 {
110 typval_T rettv;
111 typval_T argv[2];
112
113 argv[0].v_type = VAR_NUMBER;
114 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
115 argv[1].v_type = VAR_UNKNOWN;
116
117 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
118 clear_tv(&rettv);
119 }
120
121 /*
122 * Call timers that are due.
123 * Return the time in msec until the next timer is due.
124 * Returns -1 if there are no pending timers.
125 */
126 long
127 check_due_timer(void)
128 {
129 timer_T *timer;
130 timer_T *timer_next;
131 long this_due;
132 long next_due = -1;
133 proftime_T now;
134 int did_one = FALSE;
135 int need_update_screen = FALSE;
136 long current_id = last_timer_id;
137
138 // Don't run any timers while exiting or dealing with an error.
139 if (exiting || aborting())
140 return next_due;
141
142 profile_start(&now);
143 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
144 {
145 timer_next = timer->tr_next;
146
147 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
148 continue;
149 this_due = proftime_time_left(&timer->tr_due, &now);
150 if (this_due <= 1)
151 {
152 // Save and restore a lot of flags, because the timer fires while
153 // waiting for a character, which might be halfway a command.
154 int save_timer_busy = timer_busy;
155 int save_vgetc_busy = vgetc_busy;
156 int save_did_emsg = did_emsg;
157 int save_called_emsg = called_emsg;
158 int save_must_redraw = must_redraw;
159 int save_trylevel = trylevel;
160 int save_did_throw = did_throw;
161 int save_ex_pressedreturn = get_pressedreturn();
162 int save_may_garbage_collect = may_garbage_collect;
163 except_T *save_current_exception = current_exception;
164 vimvars_save_T vvsave;
165
166 // Create a scope for running the timer callback, ignoring most of
167 // the current scope, such as being inside a try/catch.
168 timer_busy = timer_busy > 0 || vgetc_busy > 0;
169 vgetc_busy = 0;
170 called_emsg = 0;
171 did_emsg = FALSE;
172 did_uncaught_emsg = FALSE;
173 must_redraw = 0;
174 trylevel = 0;
175 did_throw = FALSE;
176 current_exception = NULL;
177 may_garbage_collect = FALSE;
178 save_vimvars(&vvsave);
179
180 timer->tr_firing = TRUE;
181 timer_callback(timer);
182 timer->tr_firing = FALSE;
183
184 timer_next = timer->tr_next;
185 did_one = TRUE;
186 timer_busy = save_timer_busy;
187 vgetc_busy = save_vgetc_busy;
188 if (did_uncaught_emsg)
189 ++timer->tr_emsg_count;
190 did_emsg = save_did_emsg;
191 called_emsg = save_called_emsg;
192 trylevel = save_trylevel;
193 did_throw = save_did_throw;
194 current_exception = save_current_exception;
195 restore_vimvars(&vvsave);
196 if (must_redraw != 0)
197 need_update_screen = TRUE;
198 must_redraw = must_redraw > save_must_redraw
199 ? must_redraw : save_must_redraw;
200 set_pressedreturn(save_ex_pressedreturn);
201 may_garbage_collect = save_may_garbage_collect;
202
203 // Only fire the timer again if it repeats and stop_timer() wasn't
204 // called while inside the callback (tr_id == -1).
205 if (timer->tr_repeat != 0 && timer->tr_id != -1
206 && timer->tr_emsg_count < 3)
207 {
208 profile_setlimit(timer->tr_interval, &timer->tr_due);
209 this_due = proftime_time_left(&timer->tr_due, &now);
210 if (this_due < 1)
211 this_due = 1;
212 if (timer->tr_repeat > 0)
213 --timer->tr_repeat;
214 }
215 else
216 {
217 this_due = -1;
218 remove_timer(timer);
219 free_timer(timer);
220 }
221 }
222 if (this_due > 0 && (next_due == -1 || next_due > this_due))
223 next_due = this_due;
224 }
225
226 if (did_one)
227 redraw_after_callback(need_update_screen);
228
229 #ifdef FEAT_BEVAL_TERM
230 if (bevalexpr_due_set)
231 {
232 this_due = proftime_time_left(&bevalexpr_due, &now);
233 if (this_due <= 1)
234 {
235 bevalexpr_due_set = FALSE;
236 if (balloonEval == NULL)
237 {
238 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
239 balloonEvalForTerm = TRUE;
240 }
241 if (balloonEval != NULL)
242 {
243 general_beval_cb(balloonEval, 0);
244 setcursor();
245 out_flush();
246 }
247 }
248 else if (next_due == -1 || next_due > this_due)
249 next_due = this_due;
250 }
251 #endif
252 #ifdef FEAT_TERMINAL
253 // Some terminal windows may need their buffer updated.
254 next_due = term_check_timers(next_due, &now);
255 #endif
256
257 return current_id != last_timer_id ? 1 : next_due;
258 }
259
260 /*
261 * Find a timer by ID. Returns NULL if not found;
262 */
263 static timer_T *
264 find_timer(long id)
265 {
266 timer_T *timer;
267
268 if (id >= 0)
269 {
270 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
271 if (timer->tr_id == id)
272 return timer;
273 }
274 return NULL;
275 }
276
277
278 /*
279 * Stop a timer and delete it.
280 */
281 void
282 stop_timer(timer_T *timer)
283 {
284 if (timer->tr_firing)
285 // Free the timer after the callback returns.
286 timer->tr_id = -1;
287 else
288 {
289 remove_timer(timer);
290 free_timer(timer);
291 }
292 }
293
294 static void
295 stop_all_timers(void)
296 {
297 timer_T *timer;
298 timer_T *timer_next;
299
300 for (timer = first_timer; timer != NULL; timer = timer_next)
301 {
302 timer_next = timer->tr_next;
303 stop_timer(timer);
304 }
305 }
306
307 static void
308 add_timer_info(typval_T *rettv, timer_T *timer)
309 {
310 list_T *list = rettv->vval.v_list;
311 dict_T *dict = dict_alloc();
312 dictitem_T *di;
313 long remaining;
314 proftime_T now;
315
316 if (dict == NULL)
317 return;
318 list_append_dict(list, dict);
319
320 dict_add_number(dict, "id", timer->tr_id);
321 dict_add_number(dict, "time", (long)timer->tr_interval);
322
323 profile_start(&now);
324 remaining = proftime_time_left(&timer->tr_due, &now);
325 dict_add_number(dict, "remaining", (long)remaining);
326
327 dict_add_number(dict, "repeat",
328 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1));
329 dict_add_number(dict, "paused", (long)(timer->tr_paused));
330
331 di = dictitem_alloc((char_u *)"callback");
332 if (di != NULL)
333 {
334 if (dict_add(dict, di) == FAIL)
335 vim_free(di);
336 else
337 put_callback(&timer->tr_callback, &di->di_tv);
338 }
339 }
340
341 static void
342 add_timer_info_all(typval_T *rettv)
343 {
344 timer_T *timer;
345
346 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
347 if (timer->tr_id != -1)
348 add_timer_info(rettv, timer);
349 }
350
351 /*
352 * Mark references in partials of timers.
353 */
354 int
355 set_ref_in_timer(int copyID)
356 {
357 int abort = FALSE;
358 timer_T *timer;
359 typval_T tv;
360
361 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
362 {
363 if (timer->tr_callback.cb_partial != NULL)
364 {
365 tv.v_type = VAR_PARTIAL;
366 tv.vval.v_partial = timer->tr_callback.cb_partial;
367 }
368 else
369 {
370 tv.v_type = VAR_FUNC;
371 tv.vval.v_string = timer->tr_callback.cb_name;
372 }
373 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
374 }
375 return abort;
376 }
377
378 # if defined(EXITFREE) || defined(PROTO)
379 void
380 timer_free_all()
381 {
382 timer_T *timer;
383
384 while (first_timer != NULL)
385 {
386 timer = first_timer;
387 remove_timer(timer);
388 free_timer(timer);
389 }
390 }
391 # endif
392
393 /*
394 * "timer_info([timer])" function
395 */
396 void
397 f_timer_info(typval_T *argvars, typval_T *rettv)
398 {
399 timer_T *timer = NULL;
400
401 if (rettv_list_alloc(rettv) != OK)
402 return;
403 if (argvars[0].v_type != VAR_UNKNOWN)
404 {
405 if (argvars[0].v_type != VAR_NUMBER)
406 emsg(_(e_number_exp));
407 else
408 {
409 timer = find_timer((int)tv_get_number(&argvars[0]));
410 if (timer != NULL)
411 add_timer_info(rettv, timer);
412 }
413 }
414 else
415 add_timer_info_all(rettv);
416 }
417
418 /*
419 * "timer_pause(timer, paused)" function
420 */
421 void
422 f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
423 {
424 timer_T *timer = NULL;
425 int paused = (int)tv_get_number(&argvars[1]);
426
427 if (argvars[0].v_type != VAR_NUMBER)
428 emsg(_(e_number_exp));
429 else
430 {
431 timer = find_timer((int)tv_get_number(&argvars[0]));
432 if (timer != NULL)
433 timer->tr_paused = paused;
434 }
435 }
436
437 /*
438 * "timer_start(time, callback [, options])" function
439 */
440 void
441 f_timer_start(typval_T *argvars, typval_T *rettv)
442 {
443 long msec = (long)tv_get_number(&argvars[0]);
444 timer_T *timer;
445 int repeat = 0;
446 callback_T callback;
447 dict_T *dict;
448
449 rettv->vval.v_number = -1;
450 if (check_secure())
451 return;
452 if (argvars[2].v_type != VAR_UNKNOWN)
453 {
454 if (argvars[2].v_type != VAR_DICT
455 || (dict = argvars[2].vval.v_dict) == NULL)
456 {
457 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
458 return;
459 }
460 if (dict_find(dict, (char_u *)"repeat", -1) != NULL)
461 repeat = dict_get_number(dict, (char_u *)"repeat");
462 }
463
464 callback = get_callback(&argvars[1]);
465 if (callback.cb_name == NULL)
466 return;
467
468 timer = create_timer(msec, repeat);
469 if (timer == NULL)
470 free_callback(&callback);
471 else
472 {
473 set_callback(&timer->tr_callback, &callback);
474 rettv->vval.v_number = (varnumber_T)timer->tr_id;
475 }
476 }
477
478 /*
479 * "timer_stop(timer)" function
480 */
481 void
482 f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
483 {
484 timer_T *timer;
485
486 if (argvars[0].v_type != VAR_NUMBER)
487 {
488 emsg(_(e_number_exp));
489 return;
490 }
491 timer = find_timer((int)tv_get_number(&argvars[0]));
492 if (timer != NULL)
493 stop_timer(timer);
494 }
495
496 /*
497 * "timer_stopall()" function
498 */
499 void
500 f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
501 {
502 stop_all_timers();
503 }
504
505 # endif // FEAT_TIMERS
506
507 #endif // FEAT_EVAL
508 16
509 /* 17 /*
510 * If 'autowrite' option set, try to write the file. 18 * If 'autowrite' option set, try to write the file.
511 * Careful: autocommands may make "buf" invalid! 19 * Careful: autocommands may make "buf" invalid!
512 * 20 *