Mercurial > vim
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 * |