Package: emacs;
Reported by: Stefan Monnier <monnier <at> iro.umontreal.ca>
Date: Fri, 5 Apr 2024 20:00:02 UTC
Severity: wishlist
Tags: patch
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
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.