Skip to content

Commit caa1178

Browse files
author
Greg Hendershott
committed
Revise command process connection
Goal: Fix any remnants of issue #344. - Simplify code paths by relying on sentinels to do cleanup when the process is deleted or closed. - Analyze the remaining paths to ensure that racket--cmd-connecting-p is set t during a series of connection attempts, and, set back to nil after such a series has completed (whether succeeded, failed, or C-g by the user). - Break out the guts of racket--cmd-connect-start to a helper racket--cmd-connect-attempt. This makes it clearer that the former does checks needed at the start of the series, and the latter is called at intervals via run-at-timer to "run in the background". - Rather than setting racket--cmd-proc back to nil, leave it set to the last value, and use things like `process-status` determine if it is 'open. Provide a little helper for this: racket--cmd-open-p. - Add more (and hopefully better) comments and doc strings.
1 parent e582bbe commit caa1178

File tree

1 file changed

+89
-74
lines changed

1 file changed

+89
-74
lines changed

racket-repl.el

+89-74
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;;; racket-repl.el -*- lexical-binding: t; -*-
22

3-
;; Copyright (c) 2013-2018 by Greg Hendershott.
3+
;; Copyright (c) 2013-2019 by Greg Hendershott.
44
;; Portions Copyright (C) 1985-1986, 1999-2013 Free Software Foundation, Inc.
55
;; Image portions Copyright (C) 2012 Jose Antonio Ortega Ruiz.
66

@@ -258,15 +258,9 @@ used. See the latter for more information."
258258
(when display
259259
(display-buffer (current-buffer)))
260260
(message "Starting %s to run %s ..." racket-program run.rkt)
261-
;; Ensure command server connection closed when racket process dies.
262-
(set-process-sentinel proc
263-
(lambda (_proc event)
264-
(with-racket-repl-buffer
265-
(insert (concat "Process Racket REPL " event)))
266-
(racket--cmd-disconnect)))
267261
(set-process-coding-system proc 'utf-8 'utf-8) ;for e.g. λ
268262
(racket-repl-mode)))))
269-
(unless (racket--cmd-connected-or-connecting-p)
263+
(unless (racket--cmd-open-or-connecting-p)
270264
(racket--cmd-connect-start)))
271265

272266
(defun racket--version ()
@@ -292,10 +286,15 @@ used. See the latter for more information."
292286
;;; Connection to command process
293287

294288
(defvar racket--cmd-proc nil
295-
"Process when connection to the command server is established.")
296-
(defvar racket--cmd-buf nil
297-
"Process buffer when connection to the command server is established.")
298-
(defvar racket--cmd-connecting-p nil)
289+
"Process for talking to the command server.
290+
Use `racket--cmd-open-p' to check if it is non-nil and in an
291+
'open state.")
292+
293+
(defvar racket--cmd-connecting-p nil
294+
"True while a series of connection attempts is underway.
295+
See `racket--cmd-connect-start' and`racket--cmd-connect-finish'.
296+
Used to guard against starting more than one series of connection
297+
attempts at once.")
299298

300299
(defvar racket--cmd-nonce->callback (make-hash-table :test 'eq)
301300
"A hash from nonce to callback function.")
@@ -305,76 +304,90 @@ used. See the latter for more information."
305304
(defvar racket--cmd-connect-attempts 15)
306305
(defvar racket--cmd-connect-timeout 15)
307306

308-
(defun racket--cmd-connected-or-connecting-p ()
309-
(and (or racket--cmd-proc racket--cmd-connecting-p) t))
310-
311-
(defun racket--cmd-connect-start (&optional attempt)
312-
"Start to connect to the Racket command process.
313-
314-
If already connected, disconnects first.
315-
316-
The command server may might not be ready to accept connections,
317-
because Racket itself and our backend are still starting up.
318-
After calling this, call `racket--cmd-connect-finish' to
319-
wait for the connection to be established."
307+
(defun racket--cmd-open-p ()
308+
(and racket--cmd-proc
309+
(eq 'open (process-status racket--cmd-proc))))
310+
311+
(defun racket--cmd-open-or-connecting-p ()
312+
(or (racket--cmd-open-p)
313+
racket--cmd-connecting-p))
314+
315+
(defun racket--cmd-connect-start ()
316+
"Start a series of attempts to connect to the Racket command process.
317+
318+
The command server might not be ready to accept connections,
319+
because Racket itself and our backend are still starting up. We
320+
don't want to block Emacs while waiting. Instead: Call this to
321+
start a series of connection attempts that will \"happen in the
322+
background\" at intervals using `run-with-timer'. After this
323+
returns, then later when eventually something really must wait
324+
for the connection, to issue a command, call
325+
`racket--cmd-connect-finish'"
320326
(unless (featurep 'make-network-process '(:nowait t))
321327
(error "racket-mode needs Emacs to support the :nowait feature"))
322-
(let ((attempt (or attempt 1)))
323-
(when (= attempt 1)
324-
(racket--cmd-disconnect)
325-
(setq racket--cmd-connecting-p t))
326-
(make-network-process
327-
:name "racket-command"
328-
:host "127.0.0.1"
329-
:service racket-command-port
330-
:nowait t
331-
:sentinel
332-
(lambda (proc event)
333-
;;(message "sentinel process %S event %S attempt %s" proc event attempt)
334-
(cond
335-
((string-match-p "^open" event)
336-
(setq racket--cmd-proc proc)
337-
(setq racket--cmd-buf (generate-new-buffer
338-
(concat " " (process-name proc))))
339-
(buffer-disable-undo racket--cmd-buf)
340-
(set-process-filter proc #'racket--cmd-process-filter)
341-
(process-send-string proc (concat racket--cmd-auth "\n"))
342-
(message "Connected to %s process on port %s after %s attempt(s)"
343-
proc racket-command-port attempt))
344-
((string-match-p "^failed" event)
345-
(delete-process proc)
346-
(when (<= attempt racket--cmd-connect-attempts)
347-
(run-at-time 1.0 nil
348-
#'racket--cmd-connect-start
349-
(1+ attempt))))
350-
(t (racket--cmd-disconnect)))))))
328+
(when (racket--cmd-open-p)
329+
(error "racket--cmd-proc already open"))
330+
(when racket--cmd-connecting-p
331+
(error "already in a series attempts to connect to racket command process"))
332+
(setq racket--cmd-connecting-p t)
333+
(racket--cmd-connect-attempt 1))
334+
335+
(defun racket--cmd-connect-attempt (attempt)
336+
"Only for use by `racket--cmd-connect-start'."
337+
(setq
338+
racket--cmd-proc
339+
(make-network-process
340+
:name "racket-command"
341+
:host "127.0.0.1"
342+
:service racket-command-port
343+
:nowait t
344+
:sentinel
345+
(lambda (proc event)
346+
;;(message "sentinel got (%S %S) [attempt %s]" proc event attempt)
347+
(cond ((string-match-p "^open" event)
348+
(let ((buf (generate-new-buffer (concat "*" (process-name proc) "*"))))
349+
(set-process-buffer proc buf)
350+
(buffer-disable-undo buf))
351+
(set-process-filter proc #'racket--cmd-process-filter)
352+
(process-send-string proc (concat racket--cmd-auth "\n"))
353+
(setq racket--cmd-connecting-p nil)
354+
(message "Connected to %s process on port %s after %s attempt(s)"
355+
proc racket-command-port attempt))
356+
357+
((string-match-p "^failed" event)
358+
(delete-process proc) ;we'll get called with "deleted" event, below
359+
(if (<= attempt racket--cmd-connect-attempts)
360+
(run-at-time 1.0 nil
361+
#'racket--cmd-connect-attempt
362+
(1+ attempt))
363+
(setq racket--cmd-connecting-p nil)))
364+
365+
((or (string-match-p "^deleted" event)
366+
(string-match-p "^connection broken by remote peer" event))
367+
(clrhash racket--cmd-nonce->callback))
368+
369+
(t (message "sentinel surprised by (%S %S) [attempt %s]" proc event attempt)))))))
351370

352371
(defun racket--cmd-connect-finish ()
372+
"Call this to wait for the series of connection attempts to complete."
373+
;; Important: Every flow path must set `racket--cmd-connecting-p'
374+
;; back to nil. For example: 1. Do so before calling `error'. 2. Use
375+
;; `inhibit-quit' to handle the user getting tired of waiting and
376+
;; pressing C-g.
353377
(with-timeout (racket--cmd-connect-timeout
354378
(setq racket--cmd-connecting-p nil)
355379
(error "Could not connect to racket-command process on port %s"
356380
racket-command-port))
357-
(while (not racket--cmd-proc)
358-
(message "Still trying to connect to racket-command process on port %s ..."
359-
racket-command-port)
360-
(sit-for 0.2))
361-
(setq racket--cmd-connecting-p nil)))
362-
363-
(defun racket--cmd-disconnect ()
364-
"Disconnect from the Racket command process."
365-
(setq racket--cmd-connecting-p nil)
366-
(when racket--cmd-proc
367-
;; Sentinel calls us for "deleted" event, which we ourselves will
368-
;; trigger with the `delete-process' below. So set
369-
;; racket--cmd-proc nil before calling `delete-process'.
370-
(let ((proc (prog1 racket--cmd-proc (setq racket--cmd-proc nil)))
371-
(buf (prog1 racket--cmd-buf (setq racket--cmd-buf nil))))
372-
(delete-process proc)
373-
(kill-buffer buf)
374-
(clrhash racket--cmd-nonce->callback))))
375-
376-
(defun racket--cmd-process-filter (_proc string)
377-
(let ((buffer racket--cmd-buf))
381+
(let ((inhibit-quit t))
382+
(while (and (not quit-flag)
383+
(not (racket--cmd-open-p)))
384+
(message "Still trying to connect to racket-command process on port %s ..."
385+
racket-command-port)
386+
(sit-for 0.2))
387+
(setq racket--cmd-connecting-p nil))))
388+
389+
(defun racket--cmd-process-filter (proc string)
390+
(let ((buffer (process-buffer proc)))
378391
(when (buffer-live-p buffer)
379392
(with-current-buffer buffer
380393
(goto-char (point-max))
@@ -425,6 +438,8 @@ form."
425438
(when (and callback
426439
(not (equal callback #'ignore)))
427440
(puthash racket--cmd-nonce callback racket--cmd-nonce->callback))
441+
(unless (racket--cmd-open-p)
442+
(error "racket command process not open"))
428443
(process-send-string racket--cmd-proc
429444
(format "%S\n" (cons racket--cmd-nonce
430445
command-sexpr))))

0 commit comments

Comments
 (0)