Subject: bug#45117: 28.0.50; process-send-string mysteriously exiting non-locally when called from timer

From: Stefan Monnier <[email-masked]>
Date: Thu, 10 Dec 2020 14:46:02 -0500

>> Right.  And `while-no-input` should only wrap the execution of A, so if
>> A doesn't complete, then presumably none of C nor B will want to be
>> executed, which seems OK.
>
> We are miscommunicating.  In these programs, B needs to be atomic with
> A.  When you send things into an external process, only the most naive
> of external communication protocol replies immediately and synchronously
> to the thing you just sent.  For those super simple things, like "cat"
> and "grep", your model works.

No, I was no presuming such a simple model, actually.  I was really
thinking about "send data to the LSP server then get some answer
a second or more later".

>> closes the pipe in case we're exiting before having sent all the data
>> (that's a good idea to do also in case a bug signals an error).
> Again, this killing of the subprocess assumes the trivial case of a unix
> utility.

That's just for lack of a vocabulary to say abstractly what I meant.
I understand that in many cases you may want to keep the subprocess (and
pipe) open, in which case you'll have to do something else, but that
"something" will depend on lots of details of the circumstance.

>> The exact same problem affects all normal Elisp code when the user hits
>> C-g, so I think the better path forward is to make sure it's "easy and
>> natural" to write code which reacts correctly when it's aborted at some
>> arbitrary time.  We usually get that via `unwind-protect`, but if it's
>> not enough we should develop better solutions rather than shy away from
>> `quit`.
> I get what you're saying, but there's a presumably reason we bind
> inhibit-quit to t in timers (Eli?), and it's that that code isn't
> triggered by a direct action of he user.

Indeed, we bind inhibit-quit there because when the users hit C-g they
presumably have no idea whether a timer or process filter happens to be
running right now, so they don't actually mean "stop this timer" but
something entirely different (such as run the command `keyboard-quit`).

Note that in return we expect timers and process filters to run only for
a very short amount of time, so that we can still react to C-g promptly.

> Doing that for her in the library is violating the premise of timer
> functions as one knows them.

The contract is different for timer functions than it is for eldoc
functions, yes.  This is because the expectation is that eldoc functions
may run for a non-negligible amount of time.

Maybe we should change that so it's up to the individual eldoc function
to use `while-no-input` if it needs it, but I'm not sure we've reached
that conclusion yet ;-)

> Yes, there is that too.  While-no-input has all those Heisenbergian
> effects to add to it.  But this was no heisenberg, I think.  I was
> pressing C-n the whole time, so that's "input".

OK, so `while-no-input` did its job correctly in your case.  Good.
Now the next question is: given that the user has hit `C-n` how should
we make sure Emacs responds to it as soon as possible even though it's
currently in the middle of sending a command to an LSP subprocess?

Is this "sending" expected to never take a long time (in which case
maybe using `inhibit-quit` could be the better answer)?

What's the alternative: what could the Elisp code do to abort the
communication as quickly as possible (without leaving the subprocess in
an inconsistent state and without forcing a costly restart of that
subprocess)?  If the protocol doesn't offer any way to abort a command,
maybe it could stash the rest of the data to be sent on some list of
pending data so they'll be sent later asynchronously (and remember that
the answer to that command is probably to be ignored because the user
has moved on)?

        Stefan