branch: externals/futur
commit d2f87084d0b4a17a62109fbc3e29e7ed362d2953
Author: Stefan Monnier <[email protected]>
Commit: Stefan Monnier <[email protected]>

    Reap inactive server
    
    * futur-client.el (futur--elisp-servers-delay)
    (futur--elisp-servers-timer): New vars.
    (futur--elisp-reap-idle-servers): New function.
    (futur--elisp-launch): Use it.
    (futur--sandbox-bwrap-args): Rename from `futur--bwrap-args`.
    
    * futur-server.el (futur-server): Move to structure the code a bit better.
---
 futur-client.el | 31 ++++++++++++++++++--
 futur-server.el | 89 +++++++++++++++++++++++++++++----------------------------
 2 files changed, 74 insertions(+), 46 deletions(-)

diff --git a/futur-client.el b/futur-client.el
index a34ac39de7..a0a2792618 100644
--- a/futur-client.el
+++ b/futur-client.el
@@ -38,6 +38,11 @@ This has to be the same used by `futur-server'.")
   "Alist mapping server kinds to lists of processes.
 A server kind is a symbol.")
 
+(defvar futur--elisp-servers-delay (* 60 10)
+  "Number of seconds after which an inactive server is killed.")
+
+(defvar futur--elisp-servers-timer nil)
+
 (defun futur--elisp-process-filter (proc string)
   (cl-assert (process-get proc 'futur--kind))
   (cl-assert (memq proc (assq (process-get proc 'futur--kind)
@@ -167,8 +172,30 @@ A server kind is a symbol.")
     (process-put proc 'futur--rid 0)
     (process-put proc 'futur--last-time (float-time))
     (push proc (alist-get kind futur--elisp-servers))
+    (unless futur--elisp-servers-timer
+      (setq futur--elisp-servers-timer
+            (run-with-timer futur--elisp-servers-delay
+                            futur--elisp-servers-delay
+                            #'futur--elisp-reap-idle-servers)))
     proc))
 
+(defun futur--elisp-reap-idle-servers ()
+  (let ((time (float-time))
+        (left nil))
+    (pcase-dolist (`(,_kind . ,procs) futur--elisp-servers)
+      (dolist (proc procs)
+        (if (> (- time (process-get proc 'futur--last-time))
+               futur--elisp-servers-delay)
+            ;; No activity in during more than `futur--elisp-servers-delay'.
+            ;; FIXME: Maybe we should use different delays for the case
+            ;; where the server is really idle, or the case where we're
+            ;; waiting for an answer?
+            (delete-process proc)
+          (setq left t))))
+    (unless left
+      (cancel-timer futur--elisp-servers-timer)
+      (setq futur--elisp-servers-timer nil))))
+
 (defun futur--elisp-process-answer (proc sexp-string)
   (pcase-let* ((`(,sexp . ,end)
                 (condition-case err
@@ -282,7 +309,7 @@ A server kind is a symbol.")
 
 ;; Inspired by the code in `elpa-admin.el'.
 
-(defconst futur--bwrap-args
+(defconst futur--sandbox-bwrap-args
   '("--unshare-all"
     "--dev" "/dev"
     "--proc" "/proc"
@@ -298,7 +325,7 @@ A server kind is a symbol.")
                                ,@process-environment)))
     (futur--elisp-launch
      kind `("bwrap"
-            ,@futur--bwrap-args
+            ,@futur--sandbox-bwrap-args
             ,@(mapcan (lambda (dir)
                         (when (file-directory-p dir)
                           (let ((dir (expand-file-name dir)))
diff --git a/futur-server.el b/futur-server.el
index 2f884b97af..d7ec9aebb9 100644
--- a/futur-server.el
+++ b/futur-server.el
@@ -62,6 +62,51 @@
     (princ futur--elisp-impossible-string t)
     (terpri t)))
 
+;; (defun futur-server-call-in-context (ctxname ctx func &rest args)
+;;   (futur--obarray ctxname ctx)
+;;   (apply func args))
+
+(defun futur-server ()
+  ;; We don't need a cryptographically secure ID, but just something that's
+  ;; *very* unlikely to occur by accident elsewhere and which `read' wouldn't
+  ;; process without signaling an error.
+  (let* ((sid (format " fes:%s "
+                      (secure-hash 'sha1
+                                   (format "%S:%S:%S"
+                                           (random t) (current-time)
+                                           (emacs-pid)))))
+         (sid-sym (intern (string-trim sid))))
+    ;; Initialize the cache of obarray snapshots.
+    (futur-reset-context 'futur--server-internal nil)
+    (futur--print-stdout :ready sid)
+    (while t
+      (let ((input (condition-case err (cons :read-success (futur--read-stdin))
+                     (t err))))
+        (pcase input
+          ;; Check `sid-sym' for every request, since we may have just read
+          ;; "successfully" the garbage that follows a failed read.
+          (`(:read-success ,(pred (eq sid-sym)) ,rid ,func . ,args)
+           ;; Confirm we read successfully so the client can
+           ;; distinguish where problems come from.
+           (futur--print-stdout `(:read-success ,rid) sid)
+           (let ((result
+                  (condition-case err
+                      `(:funcall-success ,rid . ,(apply func args))
+                    (t `(:funcall-error ,rid . ,err)))))
+             (futur--print-stdout result sid)))
+          (`(:read-success . ,rest)
+           (futur--print-stdout `(:unrecognized-request . ,rest) sid))
+          (_
+           ;; FIXME: We can get an `end-of-file' error if the input line
+           ;; is not a complete sexp but also if stdin was closed.
+           ;; To distinguish the two it seems we have to look at
+           ;; the actual error string :-(.
+           (if (equal input '(end-of-file "Error reading from stdin"))
+               (kill-emacs)
+             (futur--print-stdout `(:read-error . ,input) sid))))))))
+
+;;;; Manage execution contexts
+
 (defun futur--obarray-snapshot ()
   "Return a snapshot of `obarray'.
 Does not pay attention to buffer-local values of variables."
@@ -170,49 +215,5 @@ the cache."
                 (setf (alist-get name snapshots)
                       (list target (futur--obarray-snapshot))))))))))))
 
-;; (defun futur-server-call-in-context (ctxname ctx func &rest args)
-;;   (futur--obarray ctxname ctx)
-;;   (apply func args))
-
-(defun futur-server ()
-  ;; We don't need a cryptographically secure ID, but just something that's
-  ;; *very* unlikely to occur by accident elsewhere and which `read' wouldn't
-  ;; process without signaling an error.
-  (let* ((sid (format " fes:%s "
-                      (secure-hash 'sha1
-                                   (format "%S:%S:%S"
-                                           (random t) (current-time)
-                                           (emacs-pid)))))
-         (sid-sym (intern (string-trim sid))))
-    ;; Initialize the cache of obarray snapshots.
-    (futur-reset-context 'futur--server-internal nil)
-    (futur--print-stdout :ready sid)
-    (while t
-      (let ((input (condition-case err (cons :read-success (futur--read-stdin))
-                     (t err))))
-        (pcase input
-          ;; Check `sid-sym' for every request, since we may have just read
-          ;; "successfully" the garbage that follows a failed read.
-          (`(:read-success ,(pred (eq sid-sym)) ,rid ,func . ,args)
-           ;; Confirm we read successfully so the client can
-           ;; distinguish where problems come from.
-           (futur--print-stdout `(:read-success ,rid) sid)
-           (let ((result
-                  (condition-case err
-                      `(:funcall-success ,rid . ,(apply func args))
-                    (t `(:funcall-error ,rid . ,err)))))
-             (futur--print-stdout result sid)))
-          (`(:read-success . ,rest)
-           (futur--print-stdout `(:unrecognized-request . ,rest) sid))
-          (_
-           ;; FIXME: We can get an `end-of-file' error if the input line
-           ;; is not a complete sexp but also if stdin was closed.
-           ;; To distinguish the two it seems we have to look at
-           ;; the actual error string :-(.
-           (if (equal input '(end-of-file "Error reading from stdin"))
-               (kill-emacs)
-             (futur--print-stdout `(:read-error . ,input) sid))))))))
-                                 
-
 (provide 'futur-server)
 ;;; futur-server.el ends here

Reply via email to