From 0041d6272d91baceada67aec04a0bca66a64919b Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 5 Apr 2024 14:58:53 -0400 Subject: [PATCH 1/2] (Ffuncall_later): New function to expose 'pending_funcalls' to ELisp * src/keyboard.c (pending_funcalls): Make static. (pending_funcalls_r): New var. (Ffuncall_later): New Lisp function. (Frun_pending_funcalls): New function, extracted from 'timer_check_2'. Improve the code to preserve the order in which functions were added. (timer_check_2): Use it. (syms_of_keyboard): Initialize and staticpro 'pending_funcalls_r', and defsubr 'Sfuncall_later' and 'Srun_pending_funcalls'. (syms_of_keyboard_for_pdumper): Reset 'pending_funcalls_r'. * lisp/startup.el (command-line): Run pending funcalls before exiting, in batch mode. * src/lisp.h (pending_funcalls): Remove declaration. * src/frame.c (delete_frame): * src/terminal.c (Fdelete_terminal): * src/comp.c (maybe_defer_native_compilation): Use 'Ffuncall_later'. --- etc/NEWS | 4 ++++ lisp/startup.el | 2 +- src/comp.c | 4 +--- src/frame.c | 10 ++++------ src/keyboard.c | 47 +++++++++++++++++++++++++++++++++++++---------- src/lisp.h | 1 - src/terminal.c | 6 ++---- 7 files changed, 49 insertions(+), 25 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 32cec82f970..8e8cbcdeb5d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1761,6 +1761,10 @@ correctly UTF-8 encoded. * Lisp Changes in Emacs 30.1 +** New function 'funcall-later', to run code at the next convenient time. +This is very similar to 'run-with-timer' with a 0 time argument, but cheaper +and it also works in batch mode. + ** New function 'help-fns-function-name'. For named functions, it just returns the name and otherwise it returns a short "unique" string that identifies the function. diff --git a/lisp/startup.el b/lisp/startup.el index 357a4154e4c..c105886908c 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -1630,7 +1630,7 @@ command-line :warning)))) ;; If -batch, terminate after processing the command options. - (if noninteractive (kill-emacs t)) + (when noninteractive (internal--run-pending-funcalls) (kill-emacs t)) ;; In daemon mode, start the server to allow clients to connect. ;; This is done after loading the user's init file and after diff --git a/src/comp.c b/src/comp.c index 99f51e07048..f59881a1b84 100644 --- a/src/comp.c +++ b/src/comp.c @@ -5217,9 +5217,7 @@ maybe_defer_native_compilation (Lisp_Object function_name, Fputhash (function_name, definition, Vcomp_deferred_pending_h); - pending_funcalls - = Fcons (list (Qnative__compile_async, src, Qnil, Qlate), - pending_funcalls); + CALLN (Ffuncall_later, Qnative__compile_async, src, Qnil, Qlate); } diff --git a/src/frame.c b/src/frame.c index abd6ef00901..f2ed55046d6 100644 --- a/src/frame.c +++ b/src/frame.c @@ -2138,9 +2138,8 @@ delete_frame (Lisp_Object frame, Lisp_Object force) if (NILP (Vrun_hooks) || is_tooltip_frame) ; else if (EQ (force, Qnoelisp)) - pending_funcalls - = Fcons (list3 (Qrun_hook_with_args, Qdelete_frame_functions, frame), - pending_funcalls); + CALLN (Ffuncall_later, Qrun_hook_with_args, + Qdelete_frame_functions, frame); else { #ifdef HAVE_X_WINDOWS @@ -2456,9 +2455,8 @@ delete_frame (Lisp_Object frame, Lisp_Object force) if (NILP (Vrun_hooks) || is_tooltip_frame) ; else if (EQ (force, Qnoelisp)) - pending_funcalls - = Fcons (list3 (Qrun_hook_with_args, Qafter_delete_frame_functions, frame), - pending_funcalls); + CALLN (Ffuncall_later, Qrun_hook_with_args, + Qafter_delete_frame_functions, frame); else safe_calln (Qrun_hook_with_args, Qafter_delete_frame_functions, frame); diff --git a/src/keyboard.c b/src/keyboard.c index 91faf4582fa..45f7295d5d1 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4624,10 +4624,38 @@ timer_resume_idle (void) timer_idleness_start_time = timer_last_idleness_start_time; } -/* List of elisp functions to call, delayed because they were generated in - a context where Elisp could not be safely run (e.g. redisplay, signal, +/* List of elisp functions to call, delayed e.g. because they were generated in + a context where that code could not be safely run (e.g. redisplay, signal, ...). Each element has the form (FUN . ARGS). */ -Lisp_Object pending_funcalls; +static Lisp_Object pending_funcalls, pending_funcalls_r; + +DEFUN ("funcall-later", Ffuncall_later, Sfuncall_later, 1, MANY, 0, + doc: /* Schedule FUNCTION for later execution. +It will be called a soon as possible with ARGS, but not now. */) + (ptrdiff_t nargs, Lisp_Object *args) +{ + pending_funcalls_r = Fcons (Flist (nargs, args), pending_funcalls_r); + return Qnil; +} + +DEFUN ("internal--run-pending-funcalls", Frun_pending_funcalls, Srun_pending_funcalls, 0, 0, 0, + doc: /* Run the still pending `funcall-later'. */) + (void) +{ + while (CONSP (pending_funcalls) || CONSP (pending_funcalls_r)) + if (CONSP (pending_funcalls)) + { + Lisp_Object funcall = XCAR (pending_funcalls); + pending_funcalls = XCDR (pending_funcalls); + CALLN (Fapply, funcall); + } + else + { + pending_funcalls = Fnreverse (pending_funcalls_r); + pending_funcalls_r = Qnil; + } + return Qnil; +} /* Return true if TIMER is a valid timer, placing its value into *RESULT. */ static bool @@ -4671,12 +4699,7 @@ timer_check_2 (Lisp_Object timers, Lisp_Object idle_timers) chosen_timer = Qnil; /* First run the code that was delayed. */ - while (CONSP (pending_funcalls)) - { - Lisp_Object funcall = XCAR (pending_funcalls); - pending_funcalls = XCDR (pending_funcalls); - safe_calln (Qapply, XCAR (funcall), XCDR (funcall)); - } + Frun_pending_funcalls (); if (CONSP (timers) || CONSP (idle_timers)) { @@ -12783,8 +12806,9 @@ is_ignored_event (union buffered_input_event *event) void syms_of_keyboard (void) { - pending_funcalls = Qnil; + pending_funcalls = pending_funcalls_r = Qnil; staticpro (&pending_funcalls); + staticpro (&pending_funcalls_r); Vlispy_mouse_stem = build_pure_c_string ("mouse"); staticpro (&Vlispy_mouse_stem); @@ -13084,6 +13108,8 @@ syms_of_keyboard (void) defsubr (&Scurrent_idle_time); defsubr (&Sevent_symbol_parse_modifiers); defsubr (&Sevent_convert_list); + defsubr (&Sfuncall_later); + defsubr (&Srun_pending_funcalls); defsubr (&Sinternal_handle_focus_in); defsubr (&Sread_key_sequence); defsubr (&Sread_key_sequence_vector); @@ -13855,6 +13881,7 @@ syms_of_keyboard_for_pdumper (void) early init functions see the environment they expect. */ PDUMPER_RESET_LV (pending_funcalls, Qnil); + PDUMPER_RESET_LV (pending_funcalls_r, Qnil); PDUMPER_RESET_LV (unread_switch_frame, Qnil); PDUMPER_RESET_LV (internal_last_event_frame, Qnil); PDUMPER_RESET_LV (last_command_event, Qnil); diff --git a/src/lisp.h b/src/lisp.h index 3cb4361e75e..d9308cbc6dd 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -5106,7 +5106,6 @@ fast_c_string_match_ignore_case (Lisp_Object regexp, #if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL) void handle_input_available_signal (int); #endif -extern Lisp_Object pending_funcalls; extern bool detect_input_pending (void); extern bool detect_input_pending_ignore_squeezables (void); extern bool detect_input_pending_run_timers (bool); diff --git a/src/terminal.c b/src/terminal.c index 23a5582d4d9..fd13ba90792 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -384,10 +384,8 @@ DEFUN ("delete-terminal", Fdelete_terminal, Sdelete_terminal, 0, 2, 0, if (NILP (Vrun_hooks)) ; else if (EQ (force, Qnoelisp)) - pending_funcalls - = Fcons (list3 (Qrun_hook_with_args, - Qdelete_terminal_functions, terminal), - pending_funcalls); + CALLN (Ffuncall_later, Qrun_hook_with_args, + Qdelete_terminal_functions, terminal); else safe_calln (Qrun_hook_with_args, Qdelete_terminal_functions, terminal); -- 2.43.0