guix-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[shepherd] 05/07: shepherd: Use one fiber for signal handling, and one f


From: Ludovic Courtès
Subject: [shepherd] 05/07: shepherd: Use one fiber for signal handling, and one for clients.
Date: Mon, 21 Mar 2022 18:01:38 -0400 (EDT)

civodul pushed a commit to branch wip-fibers
in repository shepherd.

commit aff030b48f6670952a5a8a0e53120ee93d935c0f
Author: Ludovic Courtès <ludo@gnu.org>
AuthorDate: Mon Mar 21 11:37:17 2022 +0100

    shepherd: Use one fiber for signal handling, and one for clients.
    
    * modules/shepherd.scm (unwind-protect): New macro.
    (call-with-server-socket): Use it instead of 'dynamic-wind'.
    (non-blocking): New procedure.
    (maybe-signal-port): Use it.
    (run-daemon): Spawn a fiber for signal handling.  Write connection
    processing loop in direct style, without 'select'.
---
 modules/shepherd.scm | 97 ++++++++++++++++++++++++++++++++--------------------
 1 file changed, 59 insertions(+), 38 deletions(-)

diff --git a/modules/shepherd.scm b/modules/shepherd.scm
index da2e509..0bbac89 100644
--- a/modules/shepherd.scm
+++ b/modules/shepherd.scm
@@ -51,23 +51,46 @@
       (listen sock 10)
       sock)))
 
+(define-syntax-rule (unwind-protect body ... conclude)
+  "Evaluate BODY... and return its result(s), but always evaluate CONCLUDE
+before leaving, even if an exception is raised.
+
+This is *not* implemented with 'dynamic-wind' in order to play well with
+delimited continuations and fibers."
+  (let ((conclusion (lambda () conclude)))
+    (catch #t
+      (lambda ()
+        (call-with-values
+            (lambda ()
+              body ...)
+          (lambda results
+            (conclusion)
+            (apply values results))))
+      (lambda args
+        (conclusion)
+        (apply throw args)))))
+
 (define (call-with-server-socket file-name proc)
   "Call PROC, passing it a listening socket at FILE-NAME and deleting the
 socket file at FILE-NAME upon exit of PROC.  Return the values of PROC."
   (let ((sock (open-server-socket file-name)))
-    (dynamic-wind
-      noop
-      (lambda () (proc sock))
-      (lambda ()
-        (close sock)
-        (catch-system-error (delete-file file-name))))))
+    (unwind-protect (proc sock)
+                    (begin
+                      (close sock)
+                      (catch-system-error (delete-file file-name))))))
+
+(define (non-blocking port)
+  "Return PORT after putting it in non-blocking mode."
+  (let ((flags (fcntl port F_GETFL)))
+    (fcntl port F_SETFL (logior O_NONBLOCK flags))
+    port))
 
 (define (maybe-signal-port signals)
   "Return a signal port for SIGNALS, using 'signalfd' on GNU/Linux, or #f if
 that is not supported."
   (catch 'system-error
     (lambda ()
-      (let ((port (signalfd -1 signals)))
+      (let ((port (non-blocking (signalfd -1 signals))))
         ;; As per the signalfd(2) man page, block SIGNALS.  The tricky bit is
         ;; that SIGNALS must be blocked for all the threads; new threads will
         ;; inherit the signal mask, but we must ensure that neither Guile's
@@ -169,37 +192,35 @@ already ~a threads running, disabling 'signalfd' support")
          ;; "Failed to autoload handle-SIGCHLD in (ice-9 readline):"
          (handle-SIGCHLD)
 
-         (let next-command ((ports (if signal-port
-                                       (list signal-port sock)
-                                       (list sock))))
-           (define (read-from sock)
-             (match (accept sock)
-               ((command-source . client-address)
-                (setvbuf command-source (buffering-mode block) 1024)
-                (process-connection command-source))
-               (_ #f)))
-
-           ;; When not using signalfd(2), there's always a time window
-           ;; before 'select' during which a handler async can be queued
-           ;; but not executed.  Work around it by exiting 'select' every
-           ;; few seconds.
-           (match (select ports (list) (list)
-                          (and (not signal-port)
-                               (if poll-services? 0.5 30)))
-             (((port _ ...) _ _)
-              (if (and signal-port (eq? port signal-port))
-                  (handle-signal-port port)
-                  (read-from sock)))
-             (_
-              ;; 'select' returned an empty set, probably due to EINTR.
-              ;; Explicitly call the SIGCHLD handler because we cannot be
-              ;; sure the async will be queued and executed before we call
-              ;; 'select' again.
-              (handle-SIGCHLD)))
-
-           (when poll-services?
-             (check-for-dead-services))
-           (next-command ports))))))
+         ;; Spawn a signal handling fiber.
+         (spawn-fiber
+          (if signal-port
+              (lambda ()
+                (let loop ()
+                  (handle-signal-port signal-port)
+                  (loop)))
+              (lambda ()
+                ;; When not using signalfd(2), there's always a time window
+                ;; before 'select' during which a handler async can be
+                ;; queued but not executed.  Work around it by exiting
+                ;; 'select' every few seconds.
+                (let loop ()
+                  (sleep (if poll-services? 0.5 30))
+                  (when poll-services?
+                    (check-for-dead-services))
+                  (loop)))))
+
+         ;; Enter some sort of a REPL for commands.
+         (let next-command ()
+           (match (accept sock)
+             ((command-source . client-address)
+              (setvbuf command-source (buffering-mode block) 1024)
+              (spawn-fiber
+               (lambda ()
+                 (process-connection command-source))))
+             (_ #f))
+
+           (next-command))))))
 
 
 ;; Main program.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]