-
Notifications
You must be signed in to change notification settings - Fork 199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Forbidden reentrant call of Tramp #859
Comments
Sorry for the long delay in looking at this. This need someone with TRAMP capabilities/expertise to debug. |
This bug seems to be locking up Emacs with a sufficiently big repository: the aforementioned "opening a new file in a different directory" deadlocks quite reliably on the vc operations to find the project root (with a pure Emacs 28.1 I've found that project.el 0.5.4 (some random version) and tramp 2.4.4.4 (the version that was bundled with Emacs 27) doesn't deadlock that much (it still does randomly), but upgrading either of them to the latest version quite reliably deadlocks. I didn't try bisecting between the versions though. The version of Eglot didn't matter, so I think it's either that project.el or tramp has a bug or that the updates exposed a bug in Eglot. I'm reliably able to deadlock on the same place in different repositories (e.g. a clone of the AFLplusplus repository, or an empty repository with just one commit that imports the whole Linux kernel sources) with a ssh tramp session connected to a local linux VM. I'd gladly help debugging this issue. |
I’m not in a position to carry out an extensive test right now, but I found there to be next to zero errors of this form when using tramp and eglot through rsh. I can try some more extensive testing later. |
I have found that most of the errors are related to ssh using ControlMaster. It is sufficient for me to set evaluate This also explains why @goranmoomin didn't have tramp freezing up in Emacs 27 - one these variables was added in Emacs 28.1. Also, it explains why I had no issues using the RSH or RCP tramp methods. Changing the variables above is a temporary fix. From my limited understanding of ControlMaster and SSH, I think the problem is that Eglot is using the same connection as other Emacs operations (for me, tramp would almost always hang when saving a buffer). This means that whenever Eglot is doing something, it will block other remote operations. A solution could be to force Eglot to open a new channel to talk to the remote language server (I think this is what Magit does). |
As I don't use TRAMP, (and don't use ssh with "ControlMaster" -- at least I think so) I only got a general idea of what you meant in this post. But I thank you very much for the analysis.
Sounds ok, but how is that performed in practice? |
A good question! I'm also new to the internals of Tramp, so I don't have an answer just yet. I will keep looking and either comment a solution here, or put in a PR. My idea is to get Eglot to spawn a new SSH connection to the remote machine, where it sends and receives information from the language server. Using the ControlMaster setting (which is an SSH setting - Tramp defaults to using it in Emacs 28), causes all processes to be sent through the same SSH connection. My Emacs-fu has space to improve. Do you know if it's possible to set a variable locally for Eglot? If so, the solution might be as simple as changing |
A local variable in Elisp isn't the best choice here, at least not if we can avoid it, but a dynamic binding may work yes. We could bind that variable around the place where Eglot makes the process connection to the remote machine. Eglot doesn't invoke TRAMP directly, but it's started as a consequence of Maybe you can play with this 100% untested patch: diff --git a/eglot.el b/eglot.el
index 2e332c470f..860fcd061f 100644
--- a/eglot.el
+++ b/eglot.el
@@ -1108,6 +1108,8 @@ Each function is passed the server as an argument")
(defvar-local eglot--cached-server nil
"A cached reference to the current EGLOT server.")
+(defvar tramp-ssh-controlmaster-options) ;; forward declare
+
(defun eglot--connect (managed-major-mode project class contact language-id)
"Connect to MANAGED-MAJOR-MODE, LANGUAGE-ID, PROJECT, CLASS and CONTACT.
This docstring appeases checkdoc, that's all."
@@ -1142,7 +1144,9 @@ This docstring appeases checkdoc, that's all."
(contact (cl-subseq contact 0 probe)))
`(:process
,(lambda ()
- (let ((default-directory default-directory))
+ (let ((default-directory default-directory)
+ (tramp-ssh-controlmaster-options
+ "-o ControlMaster=auto -o ControlPersist=no"))
(make-process
:name readable-name
:command (setq server-info (eglot--cmd contact)) |
Thank you! I will try and have a play today and report back with what I find. |
This does cause to Eglot to use a new channel when connecting over SSH! On my tests, I had 0 locks with this patch. I think we also need to add in There are a few small issues to be sorted out. First, the variable I will have a look at fixing these issues tonight. Thank you for your help! |
The forward declaration and binding of a variable that is not in a specifc Emacs version is usually unproblematic, so don't worry about Emacs 27. The Windows problem might be real, but it should be up to tramp to decide whether to honor that variable or not. |
Thankfully, the solution is to force SSH to not use ControlMaster, so it should still work for windows. |
I made a PR with this fix. I changed it slightly - it now will always not use ControlMaster, even if the user specified it in their ssh config. There also shouldn't be a problem with Windows - it still accepts a ControlMaster option. |
Just FYI, I've been running into this reentrant call issue as well. I don't use ControlMaster. I wish I had saved the backtrace (I will next time), but it seemed to be an eldoc timer scheduled from |
Ah, okay. Looking at the initial post, that does seem to be a different issue to the one I was facing. |
@gsingh93 that backtrace and your description does sound interesting. Be sure to save it somewhere next time and post it here. |
I'm not sure exactly how to reliably produce it, but it happened again today. Here's the trace: Debugger entered--Lisp error: (remote-file-error "Forbidden reentrant call of Tramp")
signal(remote-file-error ("Forbidden reentrant call of Tramp"))
tramp-error(#<process *tramp/ssh desktop*> remote-file-error "Forbidden reentrant call of Tramp")
tramp-send-string((tramp-file-name "ssh" nil nil "desktop" nil "/home/gsgx/code/ctf/imaginaryctf/pwn-polling/solve..." nil) "\\readlink --canonicalize-missing /home/gsgx/code/c...")
tramp-send-command((tramp-file-name "ssh" nil nil "desktop" nil "/home/gsgx/code/ctf/imaginaryctf/pwn-polling/solve..." nil) "\\readlink --canonicalize-missing /home/gsgx/code/c...")
tramp-send-command-and-check((tramp-file-name "ssh" nil nil "desktop" nil "/home/gsgx/code/ctf/imaginaryctf/pwn-polling/solve..." nil) "\\readlink --canonicalize-missing /home/gsgx/code/c...")
tramp-sh-handle-file-truename("/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
apply(tramp-sh-handle-file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
tramp-sh-file-name-handler(file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
apply(tramp-sh-file-name-handler file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
tramp-file-name-handler(file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
file-truename("/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
eglot--path-to-uri("/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
eglot--TextDocumentIdentifier()
eglot--VersionedTextDocumentIdentifier()
eglot--signal-textDocument/didChange()
#f(compiled-function () #<bytecode 0xab8dfa57693559d>)()
apply(#f(compiled-function () #<bytecode 0xab8dfa57693559d>) nil)
timer-event-handler([t 0 0 500000 nil #f(compiled-function () #<bytecode 0xab8dfa57693559d>) nil idle 0 nil])
accept-process-output(#<process *tramp/ssh desktop*> nil nil t)
tramp-accept-process-output(#<process *tramp/ssh desktop*>)
tramp-wait-for-regexp(#<process *tramp/ssh desktop*> nil "\\(^\\|\0\\)[^#$\n]*///3c2aa42b9dd3efe23277ca48f493e0ff...")
tramp-wait-for-output(#<process *tramp/ssh desktop*>)
tramp-send-command((tramp-file-name "ssh" nil nil "desktop" nil "/home/gsgx/code/ctf/imaginaryctf/pwn-polling/solve..." nil) "\\readlink --canonicalize-missing /home/gsgx/code/c...")
tramp-send-command-and-check((tramp-file-name "ssh" nil nil "desktop" nil "/home/gsgx/code/ctf/imaginaryctf/pwn-polling/solve..." nil) "\\readlink --canonicalize-missing /home/gsgx/code/c...")
tramp-sh-handle-file-truename("/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
apply(tramp-sh-handle-file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
tramp-sh-file-name-handler(file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
apply(tramp-sh-file-name-handler file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
tramp-file-name-handler(file-truename "/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
file-truename("/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
eglot--path-to-uri("/ssh:desktop:/home/gsgx/code/ctf/imaginaryctf/pwn-...")
eglot--TextDocumentIdentifier()
eglot--TextDocumentPositionParams()
eglot-signature-eldoc-function(#f(compiled-function (string &rest plist) #<bytecode 0xef14cb6ab714bda>))
#f(compiled-function (f) #<bytecode -0x1006840078a51029>)(eglot-signature-eldoc-function)
run-hook-wrapped(#f(compiled-function (f) #<bytecode -0x1006840078a51029>) eglot-signature-eldoc-function)
eldoc-documentation-enthusiast()
eldoc--invoke-strategy(nil)
eldoc-print-current-symbol-info()
#f(compiled-function () #<bytecode 0x1f9d6f42f35bd95d>)()
apply(#f(compiled-function () #<bytecode 0x1f9d6f42f35bd95d>) nil)
timer-event-handler([t 0 0 500000 nil #f(compiled-function () #<bytecode 0x1f9d6f42f35bd95d>) nil idle 0 nil]) |
I'm getting the same error involving similar eglot names in the stack. emacs 28.2 this ticket seems very relevant emacs-lsp/lsp-mode#2514 (comment)
|
Please, create a bug report to Emacs using In it, try to describe an experiment that leads to the error, as outlined in https://joaotavora.github.io/eglot/#Troubleshooting-Eglot. That means stripping your Emacs to the bare essentials needed to reproduce the bug. For example, is Finally, use the |
I beleive I sent bug report as you requested, but got no response or confirmation. Also can't find it and not sure if it's waiting for triage or something else...
P.S. I forgot to mention that docker-tramp seems to reproduce it more reliably than ssh-tramp |
Should be fixed upstream in Emacs's repository. See Emacs bug#61350. Upcoming Eglot 1.12 from GNU ELPA will have the workaround, and newer Tramp will also have a fix at the origin. Eventually the workaround can be removed. |
Thanks! Here's the link for the bug report if anyone is interested, it's quite an educational read: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=61350 |
Steps to reproduce:
foobar
"project" with the snippet below:C-x C-f
tofoo.cc
via TRAMP, e.g./ssh:my-remote-machine:foobar/foo/foo.cc
.[eglot] Connected! Server `clangd' now managing `c++-mode' buffers in project `foobar'
.C-x C-f
tobar.cc
.Forbidden reentrant call of Tramp
.BTW, this doesn't happen when files
foo.cc
andbar.cc
are in the same dir, e.g.foobar/foo.cc
andfoobar/bar.cc
.Details:
27.1
(with TRAMP2.5.2.2
installed from ELPA viapackage.el
)master
@8dc5180
LSP transcript - M-x eglot-events-buffer (mandatory unless Emacs inoperable)
Backtrace (mandatory, unless no error message seen or heard):
(hostname redacted out)
Minimal configuration (mandatory)
# Emacs started with: $ emacs -Q -f package-initialize -L /path/to/git-cloned/eglot -l eglot.el
The text was updated successfully, but these errors were encountered: