unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Temporarily disable `timer-event-handler'
@ 2020-02-02 23:25 Alexander Shukaev
  2020-02-03  1:36 ` Drew Adams
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Alexander Shukaev @ 2020-02-02 23:25 UTC (permalink / raw)
  To: help-gnu-emacs, emacs-devel

Hi,

I've recently discovered the dark side of Emacs: that timer events run 
by `timer-event-handler' can be triggered either at `top-level' or at 
`recursive-edit' or as part of `sit-for' (see `input-pending-p') or 
`accept-process-output' or `message' (which triggers `redisplay' and 
subsequently C function `redisplay_internal' that invokes Lisp 
interpreter machine yet again and hence potentially 
`timer-event-handler') or maybe even more.

With such plethora of possibilities for `timer-event-handler' to run 
especially in a nested manner one could even observe an interesting 
peculiarity:

- Event `A' scheduled to run ASAP.
- Event `B' scheduled to run ASAP.
- The expectation is that `B' strictly runs after `A' finishes execution.
- Well, if `A' calls `message', then according to the above, `A' could 
indirectly run `timer-event-handler' (nested) which, as a result, will 
run `B' now essentially somewhere in the middle of `A'.
- Clearly, unless taken care of, this may result in side effects and 
nasty bugs, which are difficult to track down.

Another possible side effect can be related to e.g. `let'-binding, when 
during an execution of some function some `let'-binding is performed, 
but `redisplay' is being triggered and some timer event occurs under 
that `let'-binding, which unfortunately affects it in a buggy unexpected 
way.  Also difficult to understand and hunt down.

Now, first of all, do I understand correctly that the recommended way to 
temporarily prevent timer events from happening is to `let'-bind both 
`timer-list' and `timer-idle-list' (similar to how TRAMP does it e.g. in 
`tramp-accept-process-output')?  How about a stock macro for this?

Secondly, does the above explanation, pitfall examples, and recipes to 
temporarily disable timer events in the middle of Lisp execution appear 
anywhere in the documentation?  I suspect that most users are not aware 
of this complicated design.

Finally, what's the motivation behind this design?  This looks fragile 
and error-prone to run some arbitrary code in the middle of execution of 
another code without separating their "stacks" (environment scopes), and 
even then their global side effects might interfere.  Why not only allow 
timer events to run at "safer" points of execution?  E.g. no nested 
timer events, only at `top-level', never in `redisplay', also allowed 
in-between some blessed hooks executions?



^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: Temporarily disable `timer-event-handler'
  2020-02-02 23:25 Temporarily disable `timer-event-handler' Alexander Shukaev
@ 2020-02-03  1:36 ` Drew Adams
  2020-02-03  8:23 ` Michael Albinus
  2020-02-03 14:01 ` Stefan Monnier
  2 siblings, 0 replies; 11+ messages in thread
From: Drew Adams @ 2020-02-03  1:36 UTC (permalink / raw)
  To: Alexander Shukaev, help-gnu-emacs, emacs-devel

Please don't post the same question to both help-gnu-emacs and emacs-devel.  Please pick one.  Thx.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-02 23:25 Temporarily disable `timer-event-handler' Alexander Shukaev
  2020-02-03  1:36 ` Drew Adams
@ 2020-02-03  8:23 ` Michael Albinus
  2020-02-03 14:01 ` Stefan Monnier
  2 siblings, 0 replies; 11+ messages in thread
From: Michael Albinus @ 2020-02-03  8:23 UTC (permalink / raw)
  To: Alexander Shukaev; +Cc: emacs-devel

Alexander Shukaev <emacs@Alexander.Shukaev.name> writes:

> Hi,

Hi Alexander,

> Now, first of all, do I understand correctly that the recommended way
> to temporarily prevent timer events from happening is to `let'-bind
> both `timer-list' and `timer-idle-list' (similar to how TRAMP does it
> e.g. in `tramp-accept-process-output')?  How about a stock macro for
> this?

See also https://bugs.gnu.org/29735

Unfortunately, the discussion didn't yield a reasonable answer. Instead,
it was focussed on Tramp internals which are tangent to the initial request.

Best regards, Michael.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-02 23:25 Temporarily disable `timer-event-handler' Alexander Shukaev
  2020-02-03  1:36 ` Drew Adams
  2020-02-03  8:23 ` Michael Albinus
@ 2020-02-03 14:01 ` Stefan Monnier
  2020-02-03 15:44   ` Eli Zaretskii
  2 siblings, 1 reply; 11+ messages in thread
From: Stefan Monnier @ 2020-02-03 14:01 UTC (permalink / raw)
  To: Alexander Shukaev; +Cc: help-gnu-emacs, emacs-devel

> - Event `A' scheduled to run ASAP.
> - Event `B' scheduled to run ASAP.
> - The expectation is that `B' strictly runs after `A' finishes execution.

There's the rub: currently, there's no such guarantee.  The only
"guarantee" we provide is that the processing of event A will start
before the processing of event B.

[ Note: "event" here can be a timer firing or a process output.  ]

> - Well, if `A' calls `message', then according to the above, `A' could
>   indirectly run `timer-event-handler' (nested) which, as a result, will 
>   run `B' now essentially somewhere in the middle of `A'.

If A and B are unrelated, there's no problem.
If A and B are related, there could indeed be a problem.
And notice that event B could even have been created by the processing of A.
But there can also be cases where A does want/need B to be processed
before A ends.

[ Another related situation is when A is a repeated event and a second
  A fires before the first one is done processing.  ]

> - Clearly, unless taken care of, this may result in side effects and nasty
>  bugs, which are difficult to track down.

Yup.

> Another possible side effect can be related to e.g. `let'-binding, when
>  during an execution of some function some `let'-binding is performed, 
> but `redisplay' is being triggered and some timer event occurs under that
> `let'-binding, which unfortunately affects it in a buggy unexpected way.
> Also difficult to understand and hunt down.

Oh yes, dynamically-bound vars can make that even more interesting.
Process-filters and timer events (and jit-lock, and the debugger) should
ideally be run in their own thread (and hence not affected by
dynamic-bindings in the interrupted threads).  Existing code does
occasionally depend on the current behavior, tho (e.g. in
`jit-lock-deferred-fontify` we bind `jit-lock-defer-timer` around
a call to `redisplay`), so fixing this is not completely trivial.

> Now, first of all, do I understand correctly that the recommended way to
> temporarily prevent timer events from happening is to `let'-bind both 
> `timer-list' and `timer-idle-list' (similar to how TRAMP does it e.g. in
> `tramp-accept-process-output')?  How about a stock macro for this?

The recommended way is to find a solution that doesn't involve
preventing timers from firing, so a stack macro for it doesn't sound
right (e.g. in the case of Tramp, the discussion in bug#29735 seems to
indicate that Michael agrees that disabling timers isn't "right" but
it's just what he's using so far because he wasn't able to track down
the bug in the "right" version of the code).

> I suspect that most users are not aware of this complicated design.

I don't think the design is complicated.  It's the resulting behavior
that is (arguably because the design was too naive).

> Finally, what's the motivation behind this design?  This looks fragile and
> error-prone to run some arbitrary code in the middle of execution of another
> code without separating their "stacks" (environment scopes),

It's a direct consequence of the implementation, rather than the result
of design, AFAICT.

> Why not only allow timer events to run at "safer" points of execution?
> E.g. no nested timer events,

That's probably a good idea, yes.
Probably worth giving it a try so if/what breaks.

> never in `redisplay',

Agreed.  Feel free to consider it as a bug and hence report it when
it happens.


        Stefan




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-03 14:01 ` Stefan Monnier
@ 2020-02-03 15:44   ` Eli Zaretskii
  2020-02-04 22:30     ` Alexander Shukaev
  0 siblings, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2020-02-03 15:44 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs, emacs-devel

[I removed help-gnu-emacs from the CC list.]

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Date: Mon, 03 Feb 2020 09:01:47 -0500
> Cc: help-gnu-emacs@gnu.org, emacs-devel@gnu.org
> 
> > Finally, what's the motivation behind this design?  This looks fragile and
> > error-prone to run some arbitrary code in the middle of execution of another
> > code without separating their "stacks" (environment scopes),
> 
> It's a direct consequence of the implementation, rather than the result
> of design, AFAICT.

There's also the need to run a timer as close to its expected time as
possible, so waiting for any Lisp to return might delay timers too
much.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-03 15:44   ` Eli Zaretskii
@ 2020-02-04 22:30     ` Alexander Shukaev
  2020-02-05  0:53       ` Stefan Monnier
  0 siblings, 1 reply; 11+ messages in thread
From: Alexander Shukaev @ 2020-02-04 22:30 UTC (permalink / raw)
  To: Eli Zaretskii, Stefan Monnier; +Cc: emacs-devel

On 03/02/2020 16:44, Eli Zaretskii wrote:
> [I removed help-gnu-emacs from the CC list.]
> 
>> From: Stefan Monnier <monnier@iro.umontreal.ca>
>> Date: Mon, 03 Feb 2020 09:01:47 -0500
>> Cc: help-gnu-emacs@gnu.org, emacs-devel@gnu.org
>>
>>> Finally, what's the motivation behind this design?  This looks fragile and
>>> error-prone to run some arbitrary code in the middle of execution of another
>>> code without separating their "stacks" (environment scopes),
>>
>> It's a direct consequence of the implementation, rather than the result
>> of design, AFAICT.
> 
> There's also the need to run a timer as close to its expected time as
> possible, so waiting for any Lisp to return might delay timers too
> much.
> 

Right, the current implementation looks more like coroutines, where 
certain built-in functions yield for timers to run on occasion.  This is 
indeed likely to be more performent and even useful at times, i.e. 
example from Stefan

> But there can also be cases where A does want/need B to be processed
> before A ends.

Nonetheless, given the availability of dynamic binding, I find this 
error-prone.  Is there currently a way in Emacs Lisp to run a function 
within a separate stack, i.e. free of any "user-defined" dynamic 
bindings?  I would imagine that this may be the right solution to fire 
timer events in this manner as it then does not matter whether they run 
in the middle of other routines.

Additionally, I believe the solution to fulfill

> The expectation is that `B' strictly runs after `A' finishes execution.

is also missing.  Some sort of macro (aka `ignore-timers') which 
essentially forbids yielding during the execution of the enclosed body, 
while `let'-binding both `timer-list' and `timer-idle-list' as a naive 
implementation of such does not really scale, since any inner calls to 
`cancel-timer' or otherwise scheduling other (new) timer events would 
have bad side effects.  Otherwise, should be something simple and 
similar to `inhibit-redisplay', aka `inhibit-timers' and 
`inhibit-idle-timers'.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-04 22:30     ` Alexander Shukaev
@ 2020-02-05  0:53       ` Stefan Monnier
  2020-02-05  1:21         ` Alexander Shukaev
  0 siblings, 1 reply; 11+ messages in thread
From: Stefan Monnier @ 2020-02-05  0:53 UTC (permalink / raw)
  To: Alexander Shukaev; +Cc: Eli Zaretskii, emacs-devel

>> But there can also be cases where A does want/need B to be processed
>> before A ends.
>
> Nonetheless, given the availability of dynamic binding, I find this
> error-prone.  Is there currently a way in Emacs Lisp to run a function 
> within a separate stack,

Yes and no.  Until "recently" the answer was clearly no, and now it's
kinda possible but not in a really nice way:
- using threads (which are not always available, so it can only be used conditionally).
- using `backtrace-eval` which will temporarily undo part of the
  currently installed dynamic bindings (as well as
  `with-current-buffer`s and `save-excursion`s, IIRC) before evaluating
  an expression.

> Additionally, I believe the solution to fulfill
>> The expectation is that `B' strictly runs after `A' finishes execution.
> is also missing.

Yup!

> Some sort of macro (aka `ignore-timers') which essentially
> forbids yielding during the execution of the enclosed body,

No, remember: Emacs is a Lisp machine, aka a kind of OS, so this would
mean that application A hogs the whole machine just because it wants to
prevent B from running.

Instead we need something more specific which lets A delay B without
delaying other, unrelated, events.


        Stefan




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-05  0:53       ` Stefan Monnier
@ 2020-02-05  1:21         ` Alexander Shukaev
  2020-02-05  1:30           ` Stefan Monnier
  2020-02-05 14:29           ` Eli Zaretskii
  0 siblings, 2 replies; 11+ messages in thread
From: Alexander Shukaev @ 2020-02-05  1:21 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Eli Zaretskii, emacs-devel

>> Some sort of macro (aka `ignore-timers') which essentially
>> forbids yielding during the execution of the enclosed body,
> 
> No, remember: Emacs is a Lisp machine, aka a kind of OS, so this would
> mean that application A hogs the whole machine just because it wants to
> prevent B from running.
> 
> Instead we need something more specific which lets A delay B without
> delaying other, unrelated, events.
> 

In a fine-grained scenario where one knows exactly which other timer 
(event) to delay, yes.  I was more into temporarily inhibiting all the 
timers (for whatever reason may be needed too, e.g. what Michael faces). 
  Would be cool to support both of course.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-05  1:21         ` Alexander Shukaev
@ 2020-02-05  1:30           ` Stefan Monnier
  2020-02-05 14:29           ` Eli Zaretskii
  1 sibling, 0 replies; 11+ messages in thread
From: Stefan Monnier @ 2020-02-05  1:30 UTC (permalink / raw)
  To: Alexander Shukaev; +Cc: Eli Zaretskii, emacs-devel

>> Instead we need something more specific which lets A delay B without
>> delaying other, unrelated, events.
> In a fine-grained scenario where one knows exactly which other timer (event)
> to delay, yes.  I was more into temporarily inhibiting all the timers (for
> whatever reason may be needed too, e.g. what Michael faces).

I know you are, but this would be fundamentally wrong, just like it
would be wrong for your OS to let Emacs prevent all other applications
on your machine from running.


        Stefan




^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-05  1:21         ` Alexander Shukaev
  2020-02-05  1:30           ` Stefan Monnier
@ 2020-02-05 14:29           ` Eli Zaretskii
  2020-02-05 15:09             ` Michael Albinus
  1 sibling, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2020-02-05 14:29 UTC (permalink / raw)
  To: Alexander Shukaev; +Cc: monnier, emacs-devel

> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org
> From: Alexander Shukaev <emacs@Alexander.Shukaev.name>
> Date: Wed, 5 Feb 2020 02:21:37 +0100
> 
> I was more into temporarily inhibiting all the 
> timers (for whatever reason may be needed too, e.g. what Michael faces). 
>   Would be cool to support both of course.

Micheal actually wanted to inhibit all timers but hos own, AFAIU.

And before we seriously discuss the possibility of inhibiting _all_
the timers, I'd suggest to make sure we have a clear understanding how
many features we take for granted run off timers.  Starting from
cursor-blinking and undo, for example.



^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: Temporarily disable `timer-event-handler'
  2020-02-05 14:29           ` Eli Zaretskii
@ 2020-02-05 15:09             ` Michael Albinus
  0 siblings, 0 replies; 11+ messages in thread
From: Michael Albinus @ 2020-02-05 15:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel, Alexander Shukaev, monnier

Eli Zaretskii <eliz@gnu.org> writes:

>> I was more into temporarily inhibiting all the
>> timers (for whatever reason may be needed too, e.g. what Michael faces).
>>   Would be cool to support both of course.
>
> Micheal actually wanted to inhibit all timers but hos own, AFAIU.

More precisely, I wanted to make Tramp primitives being protected
against other Tramp primitives called from timers, process sentinels and
filters, etc. Meanwhile, I found another approach protecting Tramp
against shooting itself into the knee; I don't insist any longer for
disabling all timers for the benefit of Tramp.

Best regards, Michael.



^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2020-02-05 15:09 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-02 23:25 Temporarily disable `timer-event-handler' Alexander Shukaev
2020-02-03  1:36 ` Drew Adams
2020-02-03  8:23 ` Michael Albinus
2020-02-03 14:01 ` Stefan Monnier
2020-02-03 15:44   ` Eli Zaretskii
2020-02-04 22:30     ` Alexander Shukaev
2020-02-05  0:53       ` Stefan Monnier
2020-02-05  1:21         ` Alexander Shukaev
2020-02-05  1:30           ` Stefan Monnier
2020-02-05 14:29           ` Eli Zaretskii
2020-02-05 15:09             ` Michael Albinus

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).