Package: emacs;
Reported by: Stefan Monnier <monnier <at> iro.umontreal.ca>
Date: Fri, 5 Apr 2024 20:00:02 UTC
Severity: wishlist
Tags: patch
To reply to this bug, email your comments to 70221 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Fri, 05 Apr 2024 20:00:02 GMT) Full text and rfc822 format available.Stefan Monnier <monnier <at> iro.umontreal.ca>
:bug-gnu-emacs <at> gnu.org
.
(Fri, 05 Apr 2024 20:00:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Stefan Monnier <monnier <at> iro.umontreal.ca> To: bug-gnu-emacs <at> gnu.org Subject: [PATCH] New function `funcall-later` Date: Fri, 05 Apr 2024 15:56:40 -0400
[Message part 1 (text/plain, inline)]
Tags: patch In the patch(es) below I suggest the addition of a new function `funcall-later` which exposes the already existing `pending_funcalls` mechanism to ELisp and fixes it so those are processed in the correct order and so they're not just thrown away when used in batch mode. `run-with-timer` does largely the same thing when passed a 0 timeout and no repetition, but it is a bit more costly and relies on the timers, which don't work as well in batch mode. Stefan In GNU Emacs 30.0.50 (build 1, i686-pc-linux-gnu, GTK+ Version 3.24.41, cairo version 1.18.0) of 2024-03-27 built on lechazo Repository revision: a13cfe9bb17448e104dc86b7a33761ca60297871 Repository branch: work Windowing system distributor 'The X.Org Foundation', version 11.0.12101011 System Description: Debian GNU/Linux trixie/sid Configured using: 'configure -C --enable-checking --enable-check-lisp-object-type --with-modules --with-cairo --with-tiff=ifavailable 'CFLAGS=-Wall -g3 -Og -Wno-pointer-sign' --without-native-compilation PKG_CONFIG_PATH=/home/monnier/lib/pkgconfig'
[0001-Ffuncall_later-New-function-to-expose-pending_funcal.patch (text/patch, attachment)]
[0002-lisp-Prefer-funcall-later-over-run-with-timer.patch (text/patch, inline)]
From 24d444a272bd4b42739a1883c4d5f0545dfce58a Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier <at> iro.umontreal.ca> Date: Fri, 5 Apr 2024 15:49:34 -0400 Subject: [PATCH 2/2] lisp: Prefer 'funcall-later' over 'run-with-timer' * lisp/subr.el (do-after-load-evaluation): * lisp/server.el (server-goto-toplevel): * lisp/progmodes/compile.el (compilation-error-properties): * lisp/menu-bar.el (menu-bar-mode): * lisp/jit-lock.el (jit-lock-fontify-now): * lisp/emacs-lisp/edebug.el (edebug-kill-buffer) (edebug-unload-function): Use 'funcall-later' i.s.o 'run-with-timer'. --- lisp/emacs-lisp/edebug.el | 4 ++-- lisp/jit-lock.el | 6 +++--- lisp/menu-bar.el | 6 +++--- lisp/progmodes/compile.el | 4 ++-- lisp/server.el | 2 +- lisp/subr.el | 3 +-- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el index b27ffbca908..fc76552312f 100644 --- a/lisp/emacs-lisp/edebug.el +++ b/lisp/emacs-lisp/edebug.el @@ -4023,7 +4023,7 @@ edebug-mode (defun edebug-kill-buffer () "Used on `kill-buffer-hook' when Edebug is operating in a buffer of Lisp code." - (run-with-timer 0 nil #'top-level)) + (funcall-later #'top-level)) ;;; edebug eval list mode @@ -4597,7 +4597,7 @@ edebug-unload-function (unwind-protect (abort-recursive-edit) ;; We still want to run unload-feature to completion - (run-with-idle-timer 0 nil #'(lambda () (unload-feature 'edebug))))) + (funcall-later #'unload-feature 'edebug))) (remove-hook 'called-interactively-p-functions #'edebug--called-interactively-skip) (edebug-uninstall-read-eval-functions) diff --git a/lisp/jit-lock.el b/lisp/jit-lock.el index 05c0bd847b3..14c172c1e6a 100644 --- a/lisp/jit-lock.el +++ b/lisp/jit-lock.el @@ -478,9 +478,9 @@ jit-lock-fontify-now ;; eagerly extend the refontified region with ;; jit-lock-after-change-extend-region-functions. (when (< loose-beg orig-start) - (run-with-timer 0 nil #'jit-lock-force-redisplay - (copy-marker loose-beg) - (copy-marker orig-start))) + (funcall-later #'jit-lock-force-redisplay + (copy-marker loose-beg) + (copy-marker orig-start))) ;; Skip to the end of the fully refontified part. (setq start tight-end))) diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 320fabb54cf..7a4de133b74 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -2632,9 +2632,9 @@ menu-bar-mode ;; directly. The minor-mode message "Menu Bar mode disabled" comes ;; after this function returns, overwriting any message we do here. (when (and (called-interactively-p 'interactive) (not menu-bar-mode)) - (run-with-idle-timer 0 nil 'message - (substitute-command-keys - "Menu Bar mode disabled. \ + (funcall-later #'message + (substitute-command-keys + "Menu Bar mode disabled. \ Use \\[menu-bar-mode] to make the menu bar appear.")))) ;;;###autoload diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el index 11d400e145a..952f03d3155 100644 --- a/lisp/progmodes/compile.el +++ b/lisp/progmodes/compile.el @@ -1402,8 +1402,8 @@ compilation-error-properties (when (and compilation-auto-jump-to-next (>= type compilation-skip-threshold)) (kill-local-variable 'compilation-auto-jump-to-next) - (run-with-timer 0 nil 'compilation-auto-jump - (current-buffer) (match-beginning 0))) + (funcall-later #'compilation-auto-jump + (current-buffer) (match-beginning 0))) (compilation-internal-error-properties file line end-line col end-col type fmt rule)))) diff --git a/lisp/server.el b/lisp/server.el index b65053267a6..8e3c1d0a467 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -1031,7 +1031,7 @@ server-goto-toplevel ;; to open a frame on a new display, we might end up with an unusable ;; frame because input from that display will be blocked (until exiting ;; the minibuffer). Better exit this minibuffer right away. - (run-with-timer 0 nil (lambda () (server-execute-continuation proc))) + (funcall-later #'server-execute-continuation proc) (top-level))) ;; We use various special properties on process objects: diff --git a/lisp/subr.el b/lisp/subr.el index fba70342154..20fdf8deded 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -5981,8 +5981,7 @@ do-after-load-evaluation byte-compile-current-file byte-compile-root-dir))) (byte-compile-warn "%s" msg))) - (noninteractive (funcall fun msg)) ;; No timer will be run! - (t (run-with-idle-timer 0 nil fun msg)))))) + (t (funcall-later fun msg)))))) ;; Finally, run any other hook. (run-hook-with-args 'after-load-functions abs-file)) -- 2.43.0
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Fri, 05 Apr 2024 20:51:02 GMT) Full text and rfc822 format available.Message #8 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Felician Nemeth <felician.nemeth <at> gmail.com> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Fri, 05 Apr 2024 22:49:46 +0200
> `run-with-timer` does largely the same thing when passed a 0 timeout > and no repetition, but it is a bit more costly and relies on the timers, > which don't work as well in batch mode. Do this mean (run-with-timer 0 nil ...) can always be replaced with a more efficient (funcall-later ...)? If so, would it make sense to modify run-with-time as well to call funcall-later when if its first two argument is (0 nil)? Thanks.
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Fri, 05 Apr 2024 22:47:02 GMT) Full text and rfc822 format available.Message #11 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Stefan Monnier <monnier <at> iro.umontreal.ca> To: Felician Nemeth <felician.nemeth <at> gmail.com> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Fri, 05 Apr 2024 18:44:05 -0400
> Do this mean (run-with-timer 0 nil ...) can always be replaced with a > more efficient (funcall-later ...)? If so, would it make sense to > modify run-with-time as well to call funcall-later when if its first two > argument is (0 nil)? No: beside the fact that they are run at slightly different times (which might be OK), `funcall-later` doesn't return a handle you can then use to cancel the call. Stefan
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 06:10:02 GMT) Full text and rfc822 format available.Message #14 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 09:09:25 +0300
> Date: Fri, 05 Apr 2024 15:56:40 -0400 > From: Stefan Monnier via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org> > > In the patch(es) below I suggest the addition of a new function > `funcall-later` which exposes the already existing `pending_funcalls` > mechanism to ELisp and fixes it so those are processed in the correct > order and so they're not just thrown away when used in batch mode. > > `run-with-timer` does largely the same thing when passed a 0 timeout > and no repetition, but it is a bit more costly and relies on the timers, > which don't work as well in batch mode. Thanks. We should document this in the ELisp manual, and should explain there the meaning of "at the next convenient time". (I think "convenient" here is very much misleading and thus inappropriate.)
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 06:37:02 GMT) Full text and rfc822 format available.Message #17 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 09:36:00 +0300
> Date: Fri, 05 Apr 2024 15:56:40 -0400 > From: Stefan Monnier via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org> > > +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 You are using CALLN here, whereas the previous implementation used safe_calln. Is that intended? Calling Lisp in unsafe ways in that place might not be a good idea. You didn't even inhibit QUIT. As another difference between run-with-time and this mechanism, the former took care of preserving deactivate-mark around the call, wheres funcall-later doesn't -- this is at least one difference that we should document (assuming we want it).
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 07:00:02 GMT) Full text and rfc822 format available.Message #20 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org, felician.nemeth <at> gmail.com Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 09:59:01 +0300
> Cc: 70221 <at> debbugs.gnu.org > Date: Fri, 05 Apr 2024 18:44:05 -0400 > From: Stefan Monnier via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org> > > > Do this mean (run-with-timer 0 nil ...) can always be replaced with a > > more efficient (funcall-later ...)? If so, would it make sense to > > modify run-with-time as well to call funcall-later when if its first two > > argument is (0 nil)? > > No: beside the fact that they are run at slightly different times > (which might be OK), `funcall-later` doesn't return a handle you can > then use to cancel the call. This should be mentioned in the documentation.
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 08:15:02 GMT) Full text and rfc822 format available.Message #23 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Felician Nemeth <felician.nemeth <at> gmail.com> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org, Eli Zaretskii <eliz <at> gnu.org>, Compat Development <~pkal/compat-devel <at> lists.sr.ht> Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 10:14:38 +0200
>> Cc: 70221 <at> debbugs.gnu.org >> Date: Fri, 05 Apr 2024 18:44:05 -0400 >> From: Stefan Monnier via "Bug reports for GNU Emacs, >> the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org> >> >> > Do this mean (run-with-timer 0 nil ...) can always be replaced with a >> > more efficient (funcall-later ...)? If so, would it make sense to >> > modify run-with-time as well to call funcall-later when if its first two >> > argument is (0 nil)? >> >> No: beside the fact that they are run at slightly different times >> (which might be OK), `funcall-later` doesn't return a handle you can >> then use to cancel the call. Does "running at slightly different times" mean that the compat package cannot reimplement funcall-later with run-with-time? Thanks again.
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 08:31:02 GMT) Full text and rfc822 format available.Message #26 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Sean Whitton <spwhitton <at> spwhitton.name> To: 70221 <at> debbugs.gnu.org Cc: Stefan Monnier <monnier <at> iro.umontreal.ca> Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 16:30:09 +0800
Hello, On Fri 05 Apr 2024 at 03:56pm -04, Stefan Monnier wrote: > Tags: patch > > In the patch(es) below I suggest the addition of a new function > `funcall-later` which exposes the already existing `pending_funcalls` > mechanism to ELisp and fixes it so those are processed in the correct > order and so they're not just thrown away when used in batch mode. > > `run-with-timer` does largely the same thing when passed a 0 timeout > and no repetition, but it is a bit more costly and relies on the timers, > which don't work as well in batch mode. Very nice. You could also update xref--ensure-default-directory. -- Sean Whitton
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 14:34:03 GMT) Full text and rfc822 format available.Message #29 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Stefan Monnier <monnier <at> iro.umontreal.ca> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 10:33:05 -0400
>> +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 > > You are using CALLN here, whereas the previous implementation used > safe_calln. Is that intended? Calling Lisp in unsafe ways in that > place might not be a good idea. I found it annoying that the debugger isn't brought up when a bug occurs in such delayed evaluations. Note also the subsequent call to `timer-event-handler` uses `calln` rather than `safe_calln`, so I think my original use of `safe_call2` in commit 58555d8187f3425 was a mistake. I can't see any good reason why we'd need to protect the C code from non-local exits in `timer_check_2`. > You didn't even inhibit QUIT. I'm torn on this one: OT1H while the time at which the code is run is loosely specified, it's not really asynchronous: it's run the next time Emacs "waits", which is usually about the same time as `post-command-hook` (which does use `inhibit-quit`), so there doesn't seem to be a good justification for `inhibit-quit`. OTOH when the `funcall-later` is performed from asynchronous code (e.g. from a timer), then running the delayed function call without `inhibit-quit` ends up "hoisting" that code outside of its original `inhibit-quit` context. For users such as `track-changes.el`, `inhibit-quit` is not needed/desired. But indeed when I use it in `futur.el` it's common to call `funcall-later` from process filters and timers. Usually part of the purpose of `funcall-later` is to run the code in a different (dynamic) context, but maybe `funcall-later` should preserve `inhibit-quit`? > As another difference between run-with-time and this mechanism, the > former took care of preserving deactivate-mark around the call, wheres > funcall-later doesn't This is again because `run-with-time` is designed for the general case where the delay is not 0s, so the code will be run asynchronously, wheres `funcall-later` is designed for "different time but not async". > -- this is at least one difference that we > should document (assuming we want it). AFAICT we don't document anywhere that `deactive-mark` is bound around timers, so I guess fixing that would be the best way to document the difference. >> No: beside the fact that they are run at slightly different times >> (which might be OK), `funcall-later` doesn't return a handle you can >> then use to cancel the call. > This should be mentioned in the documentation. How/where? Maybe the doc can just say that it returns nil? Other than that, it would seem odd to write something like "There is no `cancel-funcall-later`". > Thanks. We should document this in the ELisp manual, and should > explain there the meaning of "at the next convenient time". (I think > "convenient" here is very much misleading and thus inappropriate.) That's the big question, really. E.g. I currently use `funcall-later` in `track-changes.el` and in `futur.el`. I use it because it's handy. But fundamentally those two need different things: - `track-changes.el` uses `funcall-later` because it wants to delay the execution in order to combine the current buffer modification with potential soon-to-come buffer modifications. - `futur.el` uses `funcall-later` to "wake up" a piece of code that was waiting for the current event. In this case it uses `funcall-later` not because it wants to delay execution but because it wants that execution to be unaffected by the current dynamic scoping and it doesn't want that execution to block the current execution. So, if we were to change `funcall-later` so that the code gets run more promptly (e.g. via true concurrency [ let's pretend that this was actually possible and that the race-condition problems were magically solved somehow ]), it would be great for `futur.el` but would defeat the purpose for `track-changes.el`. Current users of `funcall-later` (well: of its underlying mechanism): - We use it in `comp.c` to launch the "jit" compilation of a package. - We use it in `frame.c` and `terminal.c` to run `(after-)delete-frame-functions` and `delete-terminal-functions` when the current context does not allow running ELisp. These are more like `futur.el` in the sense that the main purpose is to run the code in a different context, without blocking the current execution. Stefan
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 15:08:02 GMT) Full text and rfc822 format available.Message #32 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 18:07:04 +0300
> From: Stefan Monnier <monnier <at> iro.umontreal.ca> > Cc: 70221 <at> debbugs.gnu.org > Date: Sat, 06 Apr 2024 10:33:05 -0400 > > >> +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 > > > > You are using CALLN here, whereas the previous implementation used > > safe_calln. Is that intended? Calling Lisp in unsafe ways in that > > place might not be a good idea. > > I found it annoying that the debugger isn't brought up when a bug > occurs in such delayed evaluations. Note also the subsequent call to > `timer-event-handler` uses `calln` rather than `safe_calln`, so I think > my original use of `safe_call2` in commit 58555d8187f3425 was a mistake. timer-event-handler uses condition-case-unless-debug, so I don't see a problem with timers, and I think your use of safe_call2 was not a mistake. > I can't see any good reason why we'd need to protect the > C code from non-local exits in `timer_check_2`. Because it will prevent timers from being called? > > You didn't even inhibit QUIT. > > I'm torn on this one: > > OT1H while the time at which the code is run is loosely specified, it's > not really asynchronous: it's run the next time Emacs "waits", which is > usually about the same time as `post-command-hook` (which does use > `inhibit-quit`), so there doesn't seem to be a good justification for > `inhibit-quit`. > > OTOH when the `funcall-later` is performed from asynchronous code > (e.g. from a timer), then running the delayed function call without > `inhibit-quit` ends up "hoisting" that code outside of its original > `inhibit-quit` context. > > For users such as `track-changes.el`, `inhibit-quit` is not > needed/desired. But indeed when I use it in `futur.el` it's common to > call `funcall-later` from process filters and timers. > > Usually part of the purpose of `funcall-later` is to run the code in > a different (dynamic) context, but maybe `funcall-later` should > preserve `inhibit-quit`? From my POV, any code that runs from some background facility must inhibit QUIT, because the user can type C-g at any moment. > > As another difference between run-with-time and this mechanism, the > > former took care of preserving deactivate-mark around the call, wheres > > funcall-later doesn't > > This is again because `run-with-time` is designed for the general case > where the delay is not 0s, so the code will be run asynchronously, > wheres `funcall-later` is designed for "different time but not async". I don't think it's a wise approach. You are converting several places to using this new facility, so the prudent thing, in a program as complex and devious as Emacs, is to keep compatibility even if our hubris tempts us to think we are smarter. Bitter experience has taught me that we are not smarter, so I now prefer humility instead. > > -- this is at least one difference that we > > should document (assuming we want it). > > AFAICT we don't document anywhere that `deactive-mark` is bound > around timers, so I guess fixing that would be the best way to document > the difference. I mean where we explain the differences between zero-delay timers and funcall-later. We _will_ explain those, right? > >> No: beside the fact that they are run at slightly different times > >> (which might be OK), `funcall-later` doesn't return a handle you can > >> then use to cancel the call. > > This should be mentioned in the documentation. > > How/where? In the same place, see above, where we compare those two features. > Maybe the doc can just say that it returns nil? > Other than that, it would seem odd to write something like > "There is no `cancel-funcall-later`". Not "out of the blue", but in the context of explaining how it is different (which is important for Lisp programmers to decide which one to use in what situations), it is reasonable. > > Thanks. We should document this in the ELisp manual, and should > > explain there the meaning of "at the next convenient time". (I think > > "convenient" here is very much misleading and thus inappropriate.) > > That's the big question, really. E.g. I currently use `funcall-later` > in `track-changes.el` and in `futur.el`. I use it because it's handy. > But fundamentally those two need different things: > > - `track-changes.el` uses `funcall-later` because it wants to delay the > execution in order to combine the current buffer modification with > potential soon-to-come buffer modifications. > - `futur.el` uses `funcall-later` to "wake up" a piece of code that was > waiting for the current event. In this case it uses `funcall-later` > not because it wants to delay execution but because it wants that > execution to be unaffected by the current dynamic scoping and it > doesn't want that execution to block the current execution. > > So, if we were to change `funcall-later` so that the code gets run more > promptly (e.g. via true concurrency [ let's pretend that this was > actually possible and that the race-condition problems were magically > solved somehow ]), it would be great for `futur.el` but would defeat the > purpose for `track-changes.el`. > > Current users of `funcall-later` (well: of its underlying mechanism): > - We use it in `comp.c` to launch the "jit" compilation of a package. > - We use it in `frame.c` and `terminal.c` to run > `(after-)delete-frame-functions` and `delete-terminal-functions` when > the current context does not allow running ELisp. > > These are more like `futur.el` in the sense that the main purpose is > to run the code in a different context, without blocking the > current execution. I don't think I disagree with what you write, but I fail to see how it is relevant to the need to explain better what is that "next convenient time" when the function will run. In particular, I don't see how the different uses of funcall-later affect that "next convenient time".
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 15:47:01 GMT) Full text and rfc822 format available.Message #35 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Stefan Monnier <monnier <at> iro.umontreal.ca> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 11:46:28 -0400
> timer-event-handler uses condition-case-unless-debug, so I don't see a > problem with timers, and I think your use of safe_call2 was not a > mistake. `condition-case-unless-debug` is very different from `safe_calln`. It doesn't prevent non-local exits nor prevent showing a debugger. >> I can't see any good reason why we'd need to protect the >> C code from non-local exits in `timer_check_2`. > Because it will prevent timers from being called? Why would it? after the non-exit is caught somewhere up the stack, we'd eventually come back to `timer_check_2` and run the timer then. > From my POV, any code that runs from some background facility must > inhibit QUIT, because the user can type C-g at any moment. Agreed, and `funcall-later` doesn't run it "in the background", it runs it at the end of the current code. > You are converting several places to using this new facility, so the > prudent thing, in a program as complex and devious as Emacs, is to > keep compatibility even if our hubris tempts us to think we are > smarter. Bitter experience has taught me that we are not smarter, so > I now prefer humility instead. Ah, you're talking about the second patch. Yes, I'm not really sure we want that one. I included it more to show that there is interest in such a facility, but I don't think the risk of breakage justifies installing it. >> AFAICT we don't document anywhere that `deactive-mark` is bound >> around timers, so I guess fixing that would be the best way to document >> the difference. > I mean where we explain the differences between zero-delay timers and > funcall-later. We _will_ explain those, right? As soon as we've figured the main "when is it run" question 🙂 >> That's the big question, really. E.g. I currently use `funcall-later` >> in `track-changes.el` and in `futur.el`. I use it because it's handy. >> But fundamentally those two need different things: [...] > I don't think I disagree with what you write, but I fail to see how it > is relevant to the need to explain better what is that "next > convenient time" when the function will run. In particular, I don't > see how the different uses of funcall-later affect that "next > convenient time". It's relevant in two ways: - It determines which part of the time-behavior we should consider as something we want to document and guarantee, as opposed to the part which is incidental and which we may prefer to document as not to be relied on. - It might mean that `funcall-later` is ill-defined and should be rejected, or split into two, or should take extra arguments, or ... Stefan
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 16:17:03 GMT) Full text and rfc822 format available.Message #38 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 19:15:51 +0300
> From: Stefan Monnier <monnier <at> iro.umontreal.ca> > Cc: 70221 <at> debbugs.gnu.org > Date: Sat, 06 Apr 2024 11:46:28 -0400 > > > timer-event-handler uses condition-case-unless-debug, so I don't see a > > problem with timers, and I think your use of safe_call2 was not a > > mistake. > > `condition-case-unless-debug` is very different from `safe_calln`. > It doesn't prevent non-local exits nor prevent showing a debugger. It catches errors, doesn't it? That's what bothers me with CALLN. > >> I can't see any good reason why we'd need to protect the > >> C code from non-local exits in `timer_check_2`. > > Because it will prevent timers from being called? > > Why would it? after the non-exit is caught somewhere up the stack, we'd > eventually come back to `timer_check_2` and run the timer then. Unless the same buggy funcall-later is again in the list, right? Or do we have a machinery to disable them, like we disable faulty timers? > > From my POV, any code that runs from some background facility must > > inhibit QUIT, because the user can type C-g at any moment. > > Agreed, and `funcall-later` doesn't run it "in the background", it runs > it at the end of the current code. How is this different from running timers? > > I don't think I disagree with what you write, but I fail to see how it > > is relevant to the need to explain better what is that "next > > convenient time" when the function will run. In particular, I don't > > see how the different uses of funcall-later affect that "next > > convenient time". > > It's relevant in two ways: > > - It determines which part of the time-behavior we should consider as > something we want to document and guarantee, as opposed to the part > which is incidental and which we may prefer to document as not to be > relied on. I'm not sure I understand where you are going with this. It seems very easy to tell when the delayed functions will be called, so why are we arguing? > - It might mean that `funcall-later` is ill-defined and should be > rejected, or split into two, or should take extra arguments, or ... If the implementation changes, we will change the documentation to go along. But Lisp programmers who want to use this facility must have a pretty good idea of when the delayed code will be called, or else they are in for a bumpy ride.
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 17:33:01 GMT) Full text and rfc822 format available.Message #41 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Dmitry Gutov <dmitry <at> gutov.dev> To: Sean Whitton <spwhitton <at> spwhitton.name>, 70221 <at> debbugs.gnu.org Cc: Stefan Monnier <monnier <at> iro.umontreal.ca> Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 6 Apr 2024 20:32:21 +0300
On 06/04/2024 11:30, Sean Whitton wrote: > Very nice. You could also update xref--ensure-default-directory. With a FIXME comment, maybe. xref is in "core elpa", so not yet.
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sat, 06 Apr 2024 20:02:03 GMT) Full text and rfc822 format available.Message #44 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Stefan Monnier <monnier <at> iro.umontreal.ca> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sat, 06 Apr 2024 16:00:53 -0400
>> `condition-case-unless-debug` is very different from `safe_calln`. >> It doesn't prevent non-local exits nor prevent showing a debugger. > It catches errors, doesn't it? What's good about it? > That's what bothers me with CALLN. With CALLN the errors don't get caught by `safe_calln` so they get caught further up the stack, which seems fine by me. What problem/scenario are you thinking about? >> >> I can't see any good reason why we'd need to protect the >> >> C code from non-local exits in `timer_check_2`. >> > Because it will prevent timers from being called? >> >> Why would it? after the non-exit is caught somewhere up the stack, we'd >> eventually come back to `timer_check_2` and run the timer then. > > Unless the same buggy funcall-later is again in the list, right? No: we bump it off the list before the CALLN, specifically to avoid this problem. They don't auto-repeat like some timers. >> > From my POV, any code that runs from some background facility must >> > inhibit QUIT, because the user can type C-g at any moment. >> Agreed, and `funcall-later` doesn't run it "in the background", it runs >> it at the end of the current code. > How is this different from running timers? Non-0s timers are run in the context of some future command (or in the middle of "idle" time). `funcall-later` are run before we get to idle time or to the next user input, so if the user hits C-g during them, it's no different from hitting C-g during the main part of the command. It's realy more like `post-command-hook` (incidentally, I've been thinking that maybe we should call `internal--run-pending-funcalls` when we run `post-command-hook`). >> - It determines which part of the time-behavior we should consider as >> something we want to document and guarantee, as opposed to the part >> which is incidental and which we may prefer to document as not to be >> relied on. > I'm not sure I understand where you are going with this. It seems > very easy to tell when the delayed functions will be called, so why > are we arguing? I'm not sure the current implementation provides the behavior we want. So I think it's worthwhile thinking about what it is that we want. E.g. another implementation could be to have a separate thread running those functions. Or as mentioned above we could run them from `post-command-hook`. ... Stefan
bug-gnu-emacs <at> gnu.org
:bug#70221
; Package emacs
.
(Sun, 07 Apr 2024 05:29:01 GMT) Full text and rfc822 format available.Message #47 received at 70221 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Stefan Monnier <monnier <at> iro.umontreal.ca> Cc: 70221 <at> debbugs.gnu.org Subject: Re: bug#70221: [PATCH] New function `funcall-later` Date: Sun, 07 Apr 2024 08:28:25 +0300
> From: Stefan Monnier <monnier <at> iro.umontreal.ca> > Cc: 70221 <at> debbugs.gnu.org > Date: Sat, 06 Apr 2024 16:00:53 -0400 > > >> `condition-case-unless-debug` is very different from `safe_calln`. > >> It doesn't prevent non-local exits nor prevent showing a debugger. > > It catches errors, doesn't it? > > What's good about it? > > > That's what bothers me with CALLN. > > With CALLN the errors don't get caught by `safe_calln` so they get > caught further up the stack, which seems fine by me. > What problem/scenario are you thinking about? That we throw to top-level from a normal processing loop. Why should we not prevent that if we can? Why are you so objected to doing what we have always done there? > >> >> I can't see any good reason why we'd need to protect the > >> >> C code from non-local exits in `timer_check_2`. > >> > Because it will prevent timers from being called? > >> > >> Why would it? after the non-exit is caught somewhere up the stack, we'd > >> eventually come back to `timer_check_2` and run the timer then. > > > > Unless the same buggy funcall-later is again in the list, right? > > No: we bump it off the list before the CALLN, specifically to > avoid this problem. They don't auto-repeat like some timers. The Lisp program that initiated it could initiate another one soon enough. Why risk that when the solution is so easy? I really don't understand your objections here. Is there any harm that can possibly be done by catching errors, like timers do? If not, why not do it? > >> > From my POV, any code that runs from some background facility must > >> > inhibit QUIT, because the user can type C-g at any moment. > >> Agreed, and `funcall-later` doesn't run it "in the background", it runs > >> it at the end of the current code. > > How is this different from running timers? > > Non-0s timers are run in the context of some future command (or in the > middle of "idle" time). `funcall-later` are run before we get to idle > time or to the next user input The funcall-later functions are run in exactly the same context. Look at the code: they are called inside the same loop in timer_check_2. So the context is exactly the same. > so if the user hits C-g during them, it's no different from hitting > C-g during the main part of the command. It's realy more like > `post-command-hook` (incidentally, I've been thinking that maybe we > should call `internal--run-pending-funcalls` when we run > `post-command-hook`). Whatever happens with zero timers happens also with funcall-later functions. So the same considerations apply. From the user POV, Emacs is idle, so the user can press C-g at any moment. E.g., it is customary to press C-g when Emacs sits at the prompt in the minibuffer: both timers and funcall-later functions can be run at that time. > >> - It determines which part of the time-behavior we should consider as > >> something we want to document and guarantee, as opposed to the part > >> which is incidental and which we may prefer to document as not to be > >> relied on. > > I'm not sure I understand where you are going with this. It seems > > very easy to tell when the delayed functions will be called, so why > > are we arguing? > > I'm not sure the current implementation provides the behavior we want. > So I think it's worthwhile thinking about what it is that we want. > E.g. another implementation could be to have a separate thread running > those functions. Or as mentioned above we could run them from > `post-command-hook`. ... As long as the code is on the branch, I don't really care. But the moment it lands on master, it should have the proper documentation, and if by that time the implementation doesn't change, I insist on saying something about when these functions are called, because I think it's important. If the implementation does change, I will, of course, revisit my opinion (hopefully, during the review of the modified code that is to follow).
Stefan Kangas <stefankangas <at> gmail.com>
to control <at> debbugs.gnu.org
.
(Sun, 30 Jun 2024 05:52:02 GMT) Full text and rfc822 format available.
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.