unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
       [not found] ` <20221216085204.43B07C04961@vcs2.savannah.gnu.org>
@ 2022-12-16 14:36   ` Stefan Monnier
  2022-12-16 14:46     ` João Távora
  2022-12-17  5:37     ` F. Jason Park
  0 siblings, 2 replies; 9+ messages in thread
From: Stefan Monnier @ 2022-12-16 14:36 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

>     Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)

BTW, this has bitten us in various other cases, we should fix it once
and for all in the C code by marking the process as "busy" while we're
running the filters so we never run filters recursively.
[ If we ever bump into a case where recursive filters are needed, we
  can then add some function to remove the "busy" mark.
  Calling (accept-process-output PROC) should naturally mark PROC as
  not-busy.  ]


        Stefan




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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-16 14:36   ` emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088) Stefan Monnier
@ 2022-12-16 14:46     ` João Távora
  2022-12-16 14:56       ` Stefan Monnier
  2022-12-17  5:37     ` F. Jason Park
  1 sibling, 1 reply; 9+ messages in thread
From: João Távora @ 2022-12-16 14:46 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1141 bytes --]

On Fri, Dec 16, 2022 at 2:36 PM Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> >     Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
>
> BTW, this has bitten us in various other cases, we should fix it once
> and for all in the C code by marking the process as "busy" while we're
> running the filters so we never run filters recursively.
> [ If we ever bump into a case where recursive filters are needed, we
>   can then add some function to remove the "busy" mark.
>   Calling (accept-process-output PROC) should naturally mark PROC as
>   not-busy.  ]


I thought about that too, but I don't know what other implications
it might have. If it's somehow feasible, isn't it easier to just change
process-send-input to _not_ accept output?  That's the
only output-accepting primitive that makes sense to call from
within a process filter (and that's what happened here).  I can't
see how sit-for is useful from within a process-filter and neither
accept-process-output.  Almost certainly a bug, right? So why
can't we make it so that they just error when called under those
circumstances?

João

[-- Attachment #2: Type: text/html, Size: 1354 bytes --]

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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-16 14:46     ` João Távora
@ 2022-12-16 14:56       ` Stefan Monnier
  0 siblings, 0 replies; 9+ messages in thread
From: Stefan Monnier @ 2022-12-16 14:56 UTC (permalink / raw)
  To: João Távora; +Cc: emacs-devel

> I thought about that too, but I don't know what other implications
> it might have. If it's somehow feasible, isn't it easier to just change
> process-send-input to _not_ accept output?  That's the
> only output-accepting primitive that makes sense to call from
> within a process filter (and that's what happened here).  I can't
> see how sit-for is useful from within a process-filter and neither
> accept-process-output.  Almost certainly a bug, right? So why
> can't we make it so that they just error when called under those
> circumstances?

To me, the only thing that *is* sure is that recursive invocations of
process filters are usually not expected (and because of
non-determinism they're usually not guaranteed either), so I'd focus
on this.

Trying to figure out when `process-send-input` should or shouldn't
accept process output (and from which processes) seems a lot more murky.

And I know `accept-process-output` can be (and is) used in
process filters.  I'd be surprised if the same didn't happen for
`sit-for`.


        Stefan




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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-16 14:36   ` emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088) Stefan Monnier
  2022-12-16 14:46     ` João Távora
@ 2022-12-17  5:37     ` F. Jason Park
  2022-12-17 15:37       ` Stefan Monnier
  1 sibling, 1 reply; 9+ messages in thread
From: F. Jason Park @ 2022-12-17  5:37 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: João Távora, emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>>     Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
>
> BTW, this has bitten us in various other cases, we should fix it once
> and for all in the C code by marking the process as "busy" while we're
> running the filters so we never run filters recursively.
> [ If we ever bump into a case where recursive filters are needed, we
>   can then add some function to remove the "busy" mark.
>   Calling (accept-process-output PROC) should naturally mark PROC as
>   not-busy.  ]
>
>
>         Stefan

A possibly related discussion from the bug archive:

  https://lists.gnu.org/archive/html/bug-gnu-emacs/2022-03/msg01211.html



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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-17  5:37     ` F. Jason Park
@ 2022-12-17 15:37       ` Stefan Monnier
  2022-12-17 21:39         ` João Távora
  0 siblings, 1 reply; 9+ messages in thread
From: Stefan Monnier @ 2022-12-17 15:37 UTC (permalink / raw)
  To: F. Jason Park; +Cc: João Távora, emacs-devel

F. Jason Park [2022-12-16 21:37:36] wrote:
> Stefan Monnier <monnier@iro.umontreal.ca> writes:
>>>     Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
>>
>> BTW, this has bitten us in various other cases, we should fix it once
>> and for all in the C code by marking the process as "busy" while we're
>> running the filters so we never run filters recursively.
>> [ If we ever bump into a case where recursive filters are needed, we
>>   can then add some function to remove the "busy" mark.
>>   Calling (accept-process-output PROC) should naturally mark PROC as
>>   not-busy.  ]
>>
>>
>>         Stefan
>
> A possibly related discussion from the bug archive:
>
>   https://lists.gnu.org/archive/html/bug-gnu-emacs/2022-03/msg01211.html

Indeed.  I think this points to the need to "spawn" a piece of code to
be executed "ASAP" but not necessarily immediately.

This way when a process filter needs to send something in response to
what it received, it can just "spawn" the send, so we can return from
the process filter before the send finishes.


        Stefan




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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-17 15:37       ` Stefan Monnier
@ 2022-12-17 21:39         ` João Távora
  2022-12-18  1:57           ` Stefan Monnier
  0 siblings, 1 reply; 9+ messages in thread
From: João Távora @ 2022-12-17 21:39 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: F. Jason Park, emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> F. Jason Park [2022-12-16 21:37:36] wrote:
>> Stefan Monnier <monnier@iro.umontreal.ca> writes:
>>>>     Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
>>>
>>> BTW, this has bitten us in various other cases, we should fix it once
>>> and for all in the C code by marking the process as "busy" while we're
>>> running the filters so we never run filters recursively.
>>> [ If we ever bump into a case where recursive filters are needed, we
>>>   can then add some function to remove the "busy" mark.
>>>   Calling (accept-process-output PROC) should naturally mark PROC as
>>>   not-busy.  ]
>>>
>>>
>>>         Stefan
>>
>> A possibly related discussion from the bug archive:
>>
>>   https://lists.gnu.org/archive/html/bug-gnu-emacs/2022-03/msg01211.html
>
> Indeed.  I think this points to the need to "spawn" a piece of code to
> be executed "ASAP" but not necessarily immediately.

Would that be sth like

  (if in-process-filter (run-at-time 0 nil #'piece-of-code) (piece-of-code))

? ... supposing in-process-filter existed, of course.

>
> This way when a process filter needs to send something in response to
> what it received, it can just "spawn" the send, so we can return from
> the process filter before the send finishes.

I guess you can see it that way too.  So there are two ways to solve
this:

* only process-send-input in process filters makes sense
* all but process-send-input in process filters makes sense

I'm more into of the first persuasion, but I think it shouldn't allow
output to be accepted when called from within a process filter.  But I
guess the second option isn't bad too.  I've rehearsed a patch to
jsonrpc.el that uses this idea instead of the other kill recursion one,
and maybe it's cleaner, indeed.  Would appreciate feedback.

diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el
index 2d562610b3..e72644d317 100644
--- a/lisp/jsonrpc.el
+++ b/lisp/jsonrpc.el
@@ -418,6 +418,9 @@ initialize-instance
       (setq buffer-read-only t))
     (process-put proc 'jsonrpc-connection conn)))
 
+(defvar jsonrpc--in-process-filter nil
+  "Non-nil if inside `jsonrpc--process-filter'.")
+
 (cl-defmethod jsonrpc-connection-send ((connection jsonrpc-process-connection)
                                        &rest args
                                        &key
@@ -437,13 +440,16 @@ jsonrpc-connection-send
          (headers
           `(("Content-Length" . ,(format "%d" (string-bytes json)))
             ;; ("Content-Type" . "application/vscode-jsonrpc; charset=utf-8")
-             )))
+            ))
+         (send-it
+          (lambda ()
             (process-send-string
              (jsonrpc--process connection)
              (cl-loop for (header . value) in headers
                       concat (concat header ": " value "\r\n") into header-section
                       finally return (format "%s\r\n%s" header-section json)))
-    (jsonrpc--log-event connection message 'client)))
+            (jsonrpc--log-event connection message 'client))))
+    (if jsonrpc--in-process-filter (run-at-time 0 nil send-it) (funcall send-it))))
 
 (defun jsonrpc-process-type (conn)
   "Return the `process-type' of JSONRPC connection CONN."
@@ -548,22 +554,8 @@ jsonrpc--process-sentinel
         (delete-process proc)
         (funcall (jsonrpc--on-shutdown connection) connection)))))
 
-(defvar jsonrpc--in-process-filter nil
-  "Non-nil if inside `jsonrpc--process-filter'.")
-
 (cl-defun jsonrpc--process-filter (proc string)
   "Called when new data STRING has arrived for PROC."
-  (when jsonrpc--in-process-filter
-    ;; Problematic recursive process filters may happen if
-    ;; `jsonrpc--connection-receive', called by us, eventually calls
-    ;; client code which calls `process-send-string' (which see) to,
-    ;; say send a follow-up message.  If that happens to writes enough
-    ;; bytes for pending output to be received, we will lose JSONRPC
-    ;; messages.  In that case, remove recursiveness by re-scheduling
-    ;; ourselves to run from within a timer as soon as possible
-    ;; (bug#60088)
-    (run-at-time 0 nil #'jsonrpc--process-filter proc string)
-    (cl-return-from jsonrpc--process-filter))
   (when (buffer-live-p (process-buffer proc))
     (with-current-buffer (process-buffer proc)
       (let* ((inhibit-read-only t)








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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-17 21:39         ` João Távora
@ 2022-12-18  1:57           ` Stefan Monnier
  2022-12-18  4:08             ` João Távora
  0 siblings, 1 reply; 9+ messages in thread
From: Stefan Monnier @ 2022-12-18  1:57 UTC (permalink / raw)
  To: João Távora; +Cc: F. Jason Park, emacs-devel

>> Indeed.  I think this points to the need to "spawn" a piece of code to
>> be executed "ASAP" but not necessarily immediately.
>
> Would that be sth like
>
>   (if in-process-filter (run-at-time 0 nil #'piece-of-code) (piece-of-code))
>
> ? ... supposing in-process-filter existed, of course.

I was thinking of something more like unconditonally

    (run-at-time 0 nil #'piece-of-code)

tho abstracted behind a function.

>> This way when a process filter needs to send something in response to
>> what it received, it can just "spawn" the send, so we can return from
>> the process filter before the send finishes.
>
> I guess you can see it that way too.  So there are two ways to solve
> this:
>
> * only process-send-input in process filters makes sense
> * all but process-send-input in process filters makes sense

I assume you meant to write `process-send-string`, but I don't know what
you mean by the above (I understand neither bullets).

> I'm more into of the first persuasion, but I think it shouldn't allow
> output to be accepted when called from within a process filter.

Indeed, as a general rule doing a "blocking wait", such as
`accept-process-output` from within async code (process filter, timer,
etc..) is generally undesirable.


        Stefan




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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-18  1:57           ` Stefan Monnier
@ 2022-12-18  4:08             ` João Távora
  2022-12-18 14:32               ` Stefan Monnier
  0 siblings, 1 reply; 9+ messages in thread
From: João Távora @ 2022-12-18  4:08 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: F. Jason Park, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1851 bytes --]

On Sun, Dec 18, 2022 at 1:57 AM Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> I guess you can see it that way too.  So there are two ways to solve
> > this:
> >
> > * only process-send-input in process filters makes sense
> > * all but process-send-input in process filters makes sense
>
> I assume you meant to write `process-send-string`, but I don't know what
> you mean by the above (I understand neither bullets).
>

Yes, it's process-send-string.  I talked earlier about how I think sit-for
and
accept-process-output are two primitives that could just error when
called from within a process filter, because there's no possible reasonable
use for them, because they lead to recursive filters and recursive filters
are arguably "unreasonable".  But process-send-string (without
output-acceptance) in a filter makes sense.  That's the first bullet.

The other bullet is that a-p-output and sit-for and even recursive filters
may
make sense (if you know what you are doing, i suppose), but the common
case of sending a follow up message to a process (as a direct consequence
of the output just received) should _not_ be handled with an in-filter
p-s-string,
but with something else, like the run-at-timer idea.  In that case we can
keep
process-send-string's output acceptance.


> > I'm more into of the first persuasion, but I think it shouldn't allow
> > output to be accepted when called from within a process filter.
>
> Indeed, as a general rule doing a "blocking wait", such as
> `accept-process-output` from within async code (process filter, timer,
> etc..) is generally undesirable.
>

I agree, but process-send-string is never blocking, is it?  And anyway
if we go your 'spawn' or 'run-asap' way, we don't need to change
process-send-string's output-acceptance semantics at all.

João

[-- Attachment #2: Type: text/html, Size: 2681 bytes --]

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

* Re: emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088)
  2022-12-18  4:08             ` João Távora
@ 2022-12-18 14:32               ` Stefan Monnier
  0 siblings, 0 replies; 9+ messages in thread
From: Stefan Monnier @ 2022-12-18 14:32 UTC (permalink / raw)
  To: João Távora; +Cc: F. Jason Park, emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1427 bytes --]

> Yes, it's process-send-string.  I talked earlier about how I think
> sit-for and accept-process-output are two primitives that could just
> error when called from within a process filter, because there's no
> possible reasonable use for them, because they lead to recursive
> filters and recursive filters are arguably "unreasonable".

I agree with the sentiment, but it's not realistic given the amount of
existing code which does that, I think (and the amount of work to fix them).
We could have maybe warnings or somesuch to detect those places and start
fixing them, but I think we also need to up our game in terms of the
infrastucture we provide to help write "correct" async code (see
`futur.el` below which exposes my current ideas).

> But process-send-string (without output-acceptance) in a filter
> makes sense.

You mean "non-blocking"?  Yes, we need a non-blocking variant of `process-send-string`.

> I agree, but process-send-string is never blocking, is it?

It is, currently (which is why it can accept process output in the mean time).

> And anyway if we go your 'spawn' or 'run-asap' way, we don't need to
> change process-send-string's output-acceptance semantics at all.

We do, because when that timer triggers, your Emacs will be unresponsive
while `process-send-string` is running (which can take arbitrarily long
if the process is busy doing other things than reading our string).


        Stefan

[-- Attachment #2: futur.el --]
[-- Type: application/emacs-lisp, Size: 11105 bytes --]

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

end of thread, other threads:[~2022-12-18 14:32 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <167118072395.30479.8819833637573037468@vcs2.savannah.gnu.org>
     [not found] ` <20221216085204.43B07C04961@vcs2.savannah.gnu.org>
2022-12-16 14:36   ` emacs-29 8bf4cdcf79: Avoid recursive process filters in lisp/jsonrpc.el (bug#60088) Stefan Monnier
2022-12-16 14:46     ` João Távora
2022-12-16 14:56       ` Stefan Monnier
2022-12-17  5:37     ` F. Jason Park
2022-12-17 15:37       ` Stefan Monnier
2022-12-17 21:39         ` João Távora
2022-12-18  1:57           ` Stefan Monnier
2022-12-18  4:08             ` João Távora
2022-12-18 14:32               ` Stefan Monnier

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).