guile-commits
[Top][All Lists]
Advanced

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

[Guile-commits] 02/03: 'primitive-fork' closes and recreates the current


From: Ludovic Courtès
Subject: [Guile-commits] 02/03: 'primitive-fork' closes and recreates the current thread's 'sleep_pipe'.
Date: Sat, 8 May 2021 16:49:28 -0400 (EDT)

civodul pushed a commit to branch master
in repository guile.

commit 381291f5ff4480afbb197bf5e5a2272cfe54a386
Author: Ludovic Courtès <ludo@gnu.org>
AuthorDate: Sat May 8 21:39:15 2021 +0200

    'primitive-fork' closes and recreates the current thread's 'sleep_pipe'.
    
    Partly fixes <https://bugs.gnu.org/41948>.
    
    Previously, the child process could end up using the same 'sleep_pipe'
    as its parent, leading to a race condition handling signals.
    
    * libguile/posix.c (do_fork): New function.
    (scm_fork): Call 'do_fork' via 'scm_without_guile'.
    * test-suite/standalone/test-signal-fork: New test.
    * test-suite/standalone/Makefile.am (check_SCRIPTS, TESTS): Add it.
---
 libguile/posix.c                       | 29 +++++++++++++++-
 test-suite/standalone/Makefile.am      |  3 ++
 test-suite/standalone/test-signal-fork | 63 ++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/libguile/posix.c b/libguile/posix.c
index eaf12de..31c4ab1 100644
--- a/libguile/posix.c
+++ b/libguile/posix.c
@@ -1217,6 +1217,31 @@ SCM_DEFINE (scm_execle, "execle", 2, 0, 1,
 #undef FUNC_NAME
 
 #ifdef HAVE_FORK
+
+/* Create a process and perform post-fork cleanups in the child.  */
+static void *
+do_fork (void *ret)
+{
+  pid_t pid = fork ();
+
+  if (pid == 0)
+    {
+      /* The child process must not share its sleep pipe with the
+         parent.  Close it and create a new one.  */
+      int err;
+      scm_thread *t = SCM_I_CURRENT_THREAD;
+
+      close (t->sleep_pipe[0]);
+      close (t->sleep_pipe[1]);
+      err = pipe2 (t->sleep_pipe, O_CLOEXEC);
+      if (err != 0)
+        abort ();
+    }
+
+  * (pid_t *) ret = pid;
+  return NULL;
+}
+
 SCM_DEFINE (scm_fork, "primitive-fork", 0, 0, 0,
             (),
            "Creates a new \"child\" process by duplicating the current 
\"parent\" process.\n"
@@ -1244,7 +1269,9 @@ SCM_DEFINE (scm_fork, "primitive-fork", 0, 0, 0,
         "         further behavior unspecified.  See \"Processes\" in the\n"
         "         manual, for more information.\n"),
        scm_current_warning_port ());
-  pid = fork ();
+
+  scm_without_guile (do_fork, &pid);
+
   if (pid == -1)
     SCM_SYSERROR;
   return scm_from_int (pid);
diff --git a/test-suite/standalone/Makefile.am 
b/test-suite/standalone/Makefile.am
index 0676d26..e87100c 100644
--- a/test-suite/standalone/Makefile.am
+++ b/test-suite/standalone/Makefile.am
@@ -96,6 +96,9 @@ EXTRA_DIST += test-language.el test-language.js
 check_SCRIPTS += test-guild-compile
 TESTS += test-guild-compile
 
+check_SCRIPTS += test-signal-fork
+TESTS += test-signal-fork
+
 # test-num2integral
 test_num2integral_SOURCES = test-num2integral.c
 test_num2integral_CFLAGS = ${test_cflags}
diff --git a/test-suite/standalone/test-signal-fork 
b/test-suite/standalone/test-signal-fork
new file mode 100755
index 0000000..8151181
--- /dev/null
+++ b/test-suite/standalone/test-signal-fork
@@ -0,0 +1,63 @@
+#!/bin/sh
+guild compile "$0"
+exec guile -q -s "$0" "$@"
+!#
+;;; test-signal-fork --- Signal thread vs. fork.     -*- Scheme -*-
+;;;
+;;; Copyright (C) 2021 Free Software Foundation, Inc.
+;;;
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
USA
+
+;; Test for one of the bugs described at <https://bugs.gnu.org/41948>:
+;; when forking a Guile process that has its signal thread up and
+;; running, the 'sleep_pipe' of the main thread would end up being
+;; shared between the child and parent processes, leading to a race
+;; condition.  This test checks for the presence of that race condition.
+
+(use-modules (ice-9 match))
+
+(setvbuf (current-output-port) 'none)
+(sigaction SIGCHLD pk)                            ;start signal thread
+
+(match (primitive-fork)
+  (0
+   (format #t "child: ~a~%" (getpid))
+   (unless (zero? (sleep 5))
+     ;; If this happens, it means the select(2) call in 'scm_std_select'
+     ;; returned because one of our file descriptors had input data
+     ;; available (which shouldn't happen).
+     (format #t "child woken up!~%")
+
+     ;; Terminate the parent so the test fails.
+     (kill (getppid) SIGKILL)
+     (primitive-exit 1)))
+  (pid
+   (format #t "parent: ~a~%" (getpid))
+   (sigaction SIGALRM (lambda _
+                        (display ".")))
+
+   ;; Repeatedly send signals to self.  Previously, the thread's
+   ;; 'sleep_pipe' would wrongfully be shared between the parent and the
+   ;; child, leading to a race condition: the child could end up reading
+   ;; from the pipe in lieu of the parent.
+   (let loop ((i 50))
+     (kill (getpid) SIGALRM)
+     (usleep 50000)
+     (unless (zero? i)
+       (loop (1- i))))
+
+   ;; Terminate the child.
+   (false-if-exception (kill pid SIGKILL))
+   (format #t "~%completed~%")))



reply via email to

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