branch: elpa/cider
commit 2b941e5b32f4e9294f97eb5beaf3de5713126ffd
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Reap the sync-request callback on every exit
`nrepl-sync-request' only marked its request id completed on a "done" reply,
so an `abort-on-input' or timeout exit left the callback in
`nrepl-pending-requests' for the life of the connection. Since the hot
eldoc/completion path aborts on input routinely, this leaked a closure per
aborted lookup. Capture the id and reap it via `unwind-protect'.
---
lisp/nrepl-client.el | 76 +++++++++++++++++++++++++++-------------------------
1 file changed, 40 insertions(+), 36 deletions(-)
diff --git a/lisp/nrepl-client.el b/lisp/nrepl-client.el
index 0fd298b11b..f14524748d 100644
--- a/lisp/nrepl-client.el
+++ b/lisp/nrepl-client.el
@@ -964,42 +964,46 @@ positional shim retained for backward compatibility."
(when callback
(funcall callback resp))
(nrepl--merge response resp)))
- status)
- (nrepl-send-request request cb connection tooling)
- (while (and (not (member "done" status))
- (not (and abort-on-input
- (input-pending-p))))
- (setq status (nrepl-dict-get response "status"))
- ;; If we get a need-input message then the repl probably isn't going
- ;; anywhere, and we'll just timeout. So we forward it to the user.
- (if (and (member "need-input" status)
- nrepl-need-input-handler-function)
- (progn (funcall nrepl-need-input-handler-function (current-buffer))
- ;; If the user took a few seconds to respond, we might
- ;; unnecessarily timeout, so let's reset the timer.
- (setq time0 (current-time)))
- ;; break out in case we don't receive a response for a while
- (when (and nrepl-sync-request-timeout
- (time-less-p
- nrepl-sync-request-timeout
- (time-subtract nil time0)))
- (error "Sync nREPL request timed out %s after %s secs" request
nrepl-sync-request-timeout)))
- ;; Clean up the response, otherwise we might repeatedly ask for input.
- (nrepl-dict-put response "status" (remove "need-input" status))
- (accept-process-output nil 0.01))
- ;; If we couldn't finish, return nil.
- (when (member "done" status)
- (nrepl-dbind-response response (ex err eval-error id)
- (when (and ex err eval-error nrepl-err-handler-function)
- (funcall nrepl-err-handler-function connection))
- (when id
- (with-current-buffer connection
- (nrepl--mark-id-completed id)))
- ;; The op was declined because a ClojureScript REPL is active; abort
- ;; the calling command with a clear message (clojure-emacs/cider#2198).
- (when-let* ((msg (nrepl--clojure-only-error response)))
- (user-error "%s" msg))
- response))))
+ status
+ (id (nrepl-send-request request cb connection tooling)))
+ (unwind-protect
+ (progn
+ (while (and (not (member "done" status))
+ (not (and abort-on-input
+ (input-pending-p))))
+ (setq status (nrepl-dict-get response "status"))
+ ;; If we get a need-input message then the repl probably isn't
going
+ ;; anywhere, and we'll just timeout. So we forward it to the user.
+ (if (and (member "need-input" status)
+ nrepl-need-input-handler-function)
+ (progn (funcall nrepl-need-input-handler-function
(current-buffer))
+ ;; If the user took a few seconds to respond, we might
+ ;; unnecessarily timeout, so let's reset the timer.
+ (setq time0 (current-time)))
+ ;; break out in case we don't receive a response for a while
+ (when (and nrepl-sync-request-timeout
+ (time-less-p
+ nrepl-sync-request-timeout
+ (time-subtract nil time0)))
+ (error "Sync nREPL request timed out %s after %s secs" request
nrepl-sync-request-timeout)))
+ ;; Clean up the response, otherwise we might repeatedly ask for
input.
+ (nrepl-dict-put response "status" (remove "need-input" status))
+ (accept-process-output nil 0.01))
+ ;; If we couldn't finish, return nil.
+ (when (member "done" status)
+ (nrepl-dbind-response response (ex err eval-error)
+ (when (and ex err eval-error nrepl-err-handler-function)
+ (funcall nrepl-err-handler-function connection))
+ ;; The op was declined because a ClojureScript REPL is active;
abort
+ ;; the calling command with a clear message
(clojure-emacs/cider#2198).
+ (when-let* ((msg (nrepl--clojure-only-error response)))
+ (user-error "%s" msg))
+ response)))
+ ;; Always reap the pending callback -- even on an abort-on-input or
+ ;; timeout exit -- so it isn't leaked for the life of the connection.
+ (when (buffer-live-p connection)
+ (with-current-buffer connection
+ (nrepl--mark-id-completed id))))))
(defun nrepl-send-sync-request (request connection &optional abort-on-input
tooling callback)