Subject: bug#65837: 30.0.50; Debugger in non-main threads

From: Helmut Eller <[email-masked]>
Date: Sat, 09 Sep 2023 19:24:31 +0200

On Sat, Sep 09 2023, Eli Zaretskii wrote:

> So I think the only reasonably practical way to make this particular
> use case stable is to disable thread switching for as long as the
> debugger is active.  WDYT?

Having to disable thread switching for so long would be quite
unfortunate.

I think that is safe for the debuggee to call condition-wait or
accept-process-output on a specific pipe process.  That should be enough
to implement some form of channel/mailbox to communicate between the
debuggee and the main thread.  The debuggee would then read commands
from that channel and the main thread would send messages to that
channel instead of executing the commands.  Something like the patch in
the attachment.

--- /scratch/emacs/emacs-git/lisp/emacs-lisp/debug.el	2023-01-29 15:26:37.886911531 +0100
+++ debug.el	2023-09-09 19:20:49.039995072 +0200
@@ -283,7 +283,7 @@
 		  (message "")
 		  ;; Make sure we unbind buffer-read-only in the right buffer.
 		  (save-excursion
-		    (recursive-edit))))
+                    (debugger--recursive-edit))))
 	    (when (and (window-live-p debugger-window)
 		       (eq (window-buffer debugger-window) debugger-buffer))
 	      ;; Record height of debugger window.
@@ -314,6 +314,24 @@
 	    (set-match-data debugger-outer-match-data)))
         (setq debug-on-next-call debugger-step-after-exit)
         debugger-value))))
+
+(require 'channel)
+(defvar-local debugger--channel nil)
+(defvar-local debugger--thread nil)
+
+(defun debugger--recursive-edit ()
+  (setq debugger--thread (current-thread))
+  (cond ((eq (current-thread) main-thread)
+         (recursive-edit))
+        (t
+         (let ((ch (channel-make-channel 0)))
+           (setq debugger--channel (cdr ch))
+           (while t
+             (pcase-exhaustive (channel-receive (car ch))
+               ('(quit)
+                (message "quit received...")
+                (debugger-quit))))))))
+
 
 (defun debugger--print (obj &optional stream)
   (condition-case err
@@ -755,9 +773,12 @@
 (defun debugger-quit ()
   "Quit debugging and return to the top level."
   (interactive)
-  (if (= (recursion-depth) 0)
-      (quit-window)
-    (top-level)))
+  (cond ((eq (current-thread) debugger--thread)
+         (if (= (recursion-depth) 0)
+             (quit-window)
+           (top-level)))
+        (t
+         (channel-send debugger--channel 'quit nil))))
 
 (defun debug--implement-debug-watch (symbol newval op where)
   "Conditionally call the debugger.

Helmut