With these two patches, the clean-temp module becomes multithread-safe.

The difficult part here is to avoid that two 'close (fd)' calls, one in
normal thread code and one in a signal handler, have the effect that an
unrelated file descriptor, just open()ed in another thread, could be closed.
This issue is solved through the 'asyncsafe-spin' module, as explained in
<https://lists.gnu.org/archive/html/bug-gnulib/2020-07/msg00005.html>.


2020-07-04  Bruno Haible  <[email protected]>

        clean-temp: Make multithread-safe, part 2.
        * lib/fatal-signal.h: Include <signal.h>.
        (get_fatal_signal_set): New declaration.
        * lib/fatal-signal.c (get_fatal_signal_set): New function.
        * lib/clean-temp.c: Include asyncsafe-spin.h, gl_linked_list.h.
        (struct closeable_fd): New type.
        (fatal_signal_set): New variable.
        (init_fatal_signal_set): New function.
        (asyncsafe_close, asyncsafe_fclose_variant): New functions.
        (cleanup_action): Invoke asyncsafe_close instead of close.
        (create_temp_dir): Invoke init_fatal_signal_set.
        (register_fd): Use a plain linked list. Add a 'struct closeable_fd *'
        element.
        (unregister_fd): Remove function.
        (close_temp): Cleanup descriptors list on the fly. Invoke
        init_fatal_signal_set. Invoke asyncsafe_close instead of close.
        (fclose_variant_temp): New function.
        (fclose_temp, fwriteerror_temp, close_stream_temp): Use it.
        * modules/clean-temp (Depends-on): Add asyncsafe-spin, linked-list.

2020-07-04  Bruno Haible  <[email protected]>

        clean-temp: Make multithread-safe, part 1.
        * lib/clean-temp.c: Include glthread/lock.h.
        (cleanup_list_lock): New variable.
        (register_temp_file, unregister_temp_file, register_temp_subdir,
        unregister_temp_subdir, cleanup_temp_dir_contents): Use it.
        (create_temp_dir): Likewise. Don't free the old array.
        (descriptors_lock): New variable.
        (register_fd, unregister_fd): Use it.
        * modules/clean-temp (Depends-on): Add lock.

>From 856995cf27edc742b6aa34fd54466d2c4ed50329 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sat, 4 Jul 2020 14:39:03 +0200
Subject: [PATCH 1/2] clean-temp: Make multithread-safe, part 1.

* lib/clean-temp.c: Include glthread/lock.h.
(cleanup_list_lock): New variable.
(register_temp_file, unregister_temp_file, register_temp_subdir,
unregister_temp_subdir, cleanup_temp_dir_contents): Use it.
(create_temp_dir): Likewise. Don't free the old array.
(descriptors_lock): New variable.
(register_fd, unregister_fd): Use it.
* modules/clean-temp (Depends-on): Add lock.
---
 ChangeLog          | 12 ++++++++++++
 lib/clean-temp.c   | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 modules/clean-temp |  1 +
 3 files changed, 63 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 22c6e09..e7b3821 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2020-07-04  Bruno Haible  <[email protected]>
 
+	clean-temp: Make multithread-safe, part 1.
+	* lib/clean-temp.c: Include glthread/lock.h.
+	(cleanup_list_lock): New variable.
+	(register_temp_file, unregister_temp_file, register_temp_subdir,
+	unregister_temp_subdir, cleanup_temp_dir_contents): Use it.
+	(create_temp_dir): Likewise. Don't free the old array.
+	(descriptors_lock): New variable.
+	(register_fd, unregister_fd): Use it.
+	* modules/clean-temp (Depends-on): Add lock.
+
+2020-07-04  Bruno Haible  <[email protected]>
+
 	fatal-signal: Make multithread-safe.
 	* lib/fatal-signal.c (init_fatal_signals): Add comment.
 	(do_init_fatal_signal_set): New function, extracted from
diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index e56428c..6beea84 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -42,6 +42,7 @@
 #include "tmpdir.h"
 #include "xalloc.h"
 #include "xmalloca.h"
+#include "glthread/lock.h"
 #include "gl_xlist.h"
 #include "gl_linkedhash_list.h"
 #include "gettext.h"
@@ -95,6 +96,10 @@ struct tempdir
   gl_list_t /* <char *> */ volatile files;
 };
 
+/* Lock that protects the cleanup_list from concurrent modification in
+   different threads.  */
+gl_lock_define_initialized (static, cleanup_list_lock)
+
 /* List of all temporary directories.  */
 static struct
 {
@@ -103,6 +108,10 @@ static struct
   size_t tempdir_allocated;
 } cleanup_list /* = { NULL, 0, 0 } */;
 
+/* Lock that protects the descriptors list from concurrent modification in
+   different threads.  */
+gl_lock_define_initialized (static, descriptors_lock)
+
 /* List of all open file descriptors to temporary files.  */
 static gl_list_t /* <int> */ volatile descriptors;
 
@@ -255,6 +264,8 @@ struct temp_dir *
 create_temp_dir (const char *prefix, const char *parentdir,
                  bool cleanup_verbose)
 {
+  gl_lock_lock (cleanup_list_lock);
+
   struct tempdir * volatile *tmpdirp = NULL;
   struct tempdir *tmpdir;
   size_t i;
@@ -300,8 +311,15 @@ create_temp_dir (const char *prefix, const char *parentdir,
           cleanup_list.tempdir_allocated = new_allocated;
 
           /* Now we can free the old array.  */
+          /* No, we can't do that.  If cleanup_action is running in a different
+             thread and has already fetched the tempdir_list pointer (getting
+             old_array) but not yet accessed its i-th element, that thread may
+             crash when accessing an element of the already freed old_array
+             array.  */
+          #if 0
           if (old_array != NULL)
             free ((struct tempdir **) old_array);
+          #endif
         }
 
       tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count];
@@ -351,10 +369,12 @@ create_temp_dir (const char *prefix, const char *parentdir,
      block because then the cleanup handler would not remove the directory
      if xstrdup fails.  */
   tmpdir->dirname = xstrdup (tmpdirname);
+  gl_lock_unlock (cleanup_list_lock);
   freea (xtemplate);
   return (struct temp_dir *) tmpdir;
 
  quit:
+  gl_lock_unlock (cleanup_list_lock);
   freea (xtemplate);
   return NULL;
 }
@@ -368,9 +388,13 @@ register_temp_file (struct temp_dir *dir,
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
 
+  gl_lock_lock (cleanup_list_lock);
+
   /* Add absolute_file_name to tmpdir->files, without duplicates.  */
   if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
     gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
+
+  gl_lock_unlock (cleanup_list_lock);
 }
 
 /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
@@ -381,6 +405,9 @@ unregister_temp_file (struct temp_dir *dir,
                       const char *absolute_file_name)
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
+
+  gl_lock_lock (cleanup_list_lock);
+
   gl_list_t list = tmpdir->files;
   gl_list_node_t node;
 
@@ -392,6 +419,8 @@ unregister_temp_file (struct temp_dir *dir,
       gl_list_remove_node (list, node);
       free (old_string);
     }
+
+  gl_lock_unlock (cleanup_list_lock);
 }
 
 /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
@@ -403,9 +432,13 @@ register_temp_subdir (struct temp_dir *dir,
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
 
+  gl_lock_lock (cleanup_list_lock);
+
   /* Add absolute_dir_name to tmpdir->subdirs, without duplicates.  */
   if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
     gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
+
+  gl_lock_unlock (cleanup_list_lock);
 }
 
 /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
@@ -417,6 +450,9 @@ unregister_temp_subdir (struct temp_dir *dir,
                         const char *absolute_dir_name)
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
+
+  gl_lock_lock (cleanup_list_lock);
+
   gl_list_t list = tmpdir->subdirs;
   gl_list_node_t node;
 
@@ -428,6 +464,8 @@ unregister_temp_subdir (struct temp_dir *dir,
       gl_list_remove_node (list, node);
       free (old_string);
     }
+
+  gl_lock_unlock (cleanup_list_lock);
 }
 
 /* Remove a file, with optional error message.
@@ -488,6 +526,7 @@ cleanup_temp_subdir (struct temp_dir *dir,
 }
 
 /* Remove all registered files and subdirectories inside DIR.
+   Only to be called with cleanup_list_lock locked.
    Return 0 upon success, or -1 if there was some problem.  */
 int
 cleanup_temp_dir_contents (struct temp_dir *dir)
@@ -536,6 +575,8 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
 int
 cleanup_temp_dir (struct temp_dir *dir)
 {
+  gl_lock_lock (cleanup_list_lock);
+
   struct tempdir *tmpdir = (struct tempdir *)dir;
   int err = 0;
   size_t i;
@@ -561,6 +602,7 @@ cleanup_temp_dir (struct temp_dir *dir)
         gl_list_free (tmpdir->subdirs);
         free (tmpdir->dirname);
         free (tmpdir);
+        gl_lock_unlock (cleanup_list_lock);
         return err;
       }
 
@@ -605,16 +647,22 @@ supports_delete_on_close ()
 static void
 register_fd (int fd)
 {
+  gl_lock_lock (descriptors_lock);
+
   if (descriptors == NULL)
     descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL,
                                         false);
   gl_list_add_first (descriptors, (void *) (uintptr_t) fd);
+
+  gl_lock_unlock (descriptors_lock);
 }
 
 /* Unregister a file descriptor to be closed.  */
 static void
 unregister_fd (int fd)
 {
+  gl_lock_lock (descriptors_lock);
+
   gl_list_t fds = descriptors;
   gl_list_node_t node;
 
@@ -626,6 +674,8 @@ unregister_fd (int fd)
     /* descriptors should already contain fd.  */
     abort ();
   gl_list_remove_node (fds, node);
+
+  gl_lock_unlock (descriptors_lock);
 }
 
 /* Open a temporary file in a temporary directory.
diff --git a/modules/clean-temp b/modules/clean-temp
index abed9b9..c1d5c1c 100644
--- a/modules/clean-temp
+++ b/modules/clean-temp
@@ -9,6 +9,7 @@ Depends-on:
 stdbool
 stdint
 unistd
+lock
 error
 fatal-signal
 open
-- 
2.7.4

>From 9d1ad9c4df04071810d04dcc3142dfdeb5a5f892 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sat, 4 Jul 2020 14:39:09 +0200
Subject: [PATCH 2/2] clean-temp: Make multithread-safe, part 2.

* lib/fatal-signal.h: Include <signal.h>.
(get_fatal_signal_set): New declaration.
* lib/fatal-signal.c (get_fatal_signal_set): New function.
* lib/clean-temp.c: Include asyncsafe-spin.h, gl_linked_list.h.
(struct closeable_fd): New type.
(fatal_signal_set): New variable.
(init_fatal_signal_set): New function.
(asyncsafe_close, asyncsafe_fclose_variant): New functions.
(cleanup_action): Invoke asyncsafe_close instead of close.
(create_temp_dir): Invoke init_fatal_signal_set.
(register_fd): Use a plain linked list. Add a 'struct closeable_fd *'
element.
(unregister_fd): Remove function.
(close_temp): Cleanup descriptors list on the fly. Invoke
init_fatal_signal_set. Invoke asyncsafe_close instead of close.
(fclose_variant_temp): New function.
(fclose_temp, fwriteerror_temp, close_stream_temp): Use it.
* modules/clean-temp (Depends-on): Add asyncsafe-spin, linked-list.
---
 ChangeLog          |  22 ++++
 lib/clean-temp.c   | 303 +++++++++++++++++++++++++++++++++++++++--------------
 lib/fatal-signal.c |   7 ++
 lib/fatal-signal.h |  11 ++
 modules/clean-temp |   2 +
 5 files changed, 268 insertions(+), 77 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e7b3821..d49c9d7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2020-07-04  Bruno Haible  <[email protected]>
 
+	clean-temp: Make multithread-safe, part 2.
+	* lib/fatal-signal.h: Include <signal.h>.
+	(get_fatal_signal_set): New declaration.
+	* lib/fatal-signal.c (get_fatal_signal_set): New function.
+	* lib/clean-temp.c: Include asyncsafe-spin.h, gl_linked_list.h.
+	(struct closeable_fd): New type.
+	(fatal_signal_set): New variable.
+	(init_fatal_signal_set): New function.
+	(asyncsafe_close, asyncsafe_fclose_variant): New functions.
+	(cleanup_action): Invoke asyncsafe_close instead of close.
+	(create_temp_dir): Invoke init_fatal_signal_set.
+	(register_fd): Use a plain linked list. Add a 'struct closeable_fd *'
+	element.
+	(unregister_fd): Remove function.
+	(close_temp): Cleanup descriptors list on the fly. Invoke
+	init_fatal_signal_set. Invoke asyncsafe_close instead of close.
+	(fclose_variant_temp): New function.
+	(fclose_temp, fwriteerror_temp, close_stream_temp): Use it.
+	* modules/clean-temp (Depends-on): Add asyncsafe-spin, linked-list.
+
+2020-07-04  Bruno Haible  <[email protected]>
+
 	clean-temp: Make multithread-safe, part 1.
 	* lib/clean-temp.c: Include glthread/lock.h.
 	(cleanup_list_lock): New variable.
diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index 6beea84..4674537 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -38,6 +38,7 @@
 
 #include "error.h"
 #include "fatal-signal.h"
+#include "asyncsafe-spin.h"
 #include "pathmax.h"
 #include "tmpdir.h"
 #include "xalloc.h"
@@ -45,6 +46,7 @@
 #include "glthread/lock.h"
 #include "gl_xlist.h"
 #include "gl_linkedhash_list.h"
+#include "gl_linked_list.h"
 #include "gettext.h"
 #if GNULIB_FWRITEERROR
 # include "fwriteerror.h"
@@ -108,12 +110,28 @@ static struct
   size_t tempdir_allocated;
 } cleanup_list /* = { NULL, 0, 0 } */;
 
+/* A file descriptor to be closed.
+   In multithreaded programs, it is forbidden to close the same fd twice,
+   because you never know what unrelated open() calls are being executed in
+   other threads. So, the 'close (fd)' must be guarded by a once-only guard.  */
+struct closeable_fd
+{
+  /* The file descriptor to close.  */
+  int volatile fd;
+  /* Set to true when it has been closed.  */
+  bool volatile closed;
+  /* Lock that protects the fd from being closed twice.  */
+  asyncsafe_spinlock_t lock;
+  /* Tells whether this list element has been done and can be freed.  */
+  bool volatile done;
+};
+
 /* Lock that protects the descriptors list from concurrent modification in
    different threads.  */
 gl_lock_define_initialized (static, descriptors_lock)
 
 /* List of all open file descriptors to temporary files.  */
-static gl_list_t /* <int> */ volatile descriptors;
+static gl_list_t /* <closeable_fd *> */ volatile descriptors;
 
 
 /* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
@@ -193,6 +211,84 @@ string_hash (const void *x)
 }
 
 
+/* The set of fatal signal handlers.
+   Cached here because we are not allowed to call get_fatal_signal_set ()
+   from a signal handler.  */
+static const sigset_t *fatal_signal_set /* = NULL */;
+
+static void
+init_fatal_signal_set (void)
+{
+  if (fatal_signal_set == NULL)
+    fatal_signal_set = get_fatal_signal_set ();
+}
+
+
+/* Close a file descriptor.
+   Avoids race conditions with normal thread code or signal-handler code that
+   might want to close the same file descriptor.  */
+static _GL_ASYNC_SAFE int
+asyncsafe_close (struct closeable_fd *element)
+{
+  sigset_t saved_mask;
+  int ret;
+  int saved_errno;
+
+  asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
+  if (!element->closed)
+    {
+      ret = close (element->fd);
+      saved_errno = errno;
+      element->closed = true;
+    }
+  else
+    {
+      ret = 0;
+      saved_errno = 0;
+    }
+  asyncsafe_spin_unlock (&element->lock, &saved_mask);
+  element->done = true;
+
+  errno = saved_errno;
+  return ret;
+}
+
+/* Close a file descriptor and the stream that contains it.
+   Avoids race conditions with signal-handler code that might want to close the
+   same file descriptor.  */
+static int
+asyncsafe_fclose_variant (struct closeable_fd *element, FILE *fp,
+                          int (*fclose_variant) (FILE *))
+{
+  if (fileno (fp) != element->fd)
+    abort ();
+
+  /* Flush buffered data first, to minimize the duration of the spin lock.  */
+  fflush (fp);
+
+  sigset_t saved_mask;
+  int ret;
+  int saved_errno;
+
+  asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
+  if (!element->closed)
+    {
+      ret = fclose_variant (fp); /* invokes close (element->fd) */
+      saved_errno = errno;
+      element->closed = true;
+    }
+  else
+    {
+      ret = 0;
+      saved_errno = 0;
+    }
+  asyncsafe_spin_unlock (&element->lock, &saved_mask);
+  element->done = true;
+
+  errno = saved_errno;
+  return ret;
+}
+
 /* The signal handler.  It gets called asynchronously.  */
 static _GL_ASYNC_SAFE void
 cleanup_action (int sig _GL_UNUSED)
@@ -211,8 +307,7 @@ cleanup_action (int sig _GL_UNUSED)
         iter = gl_list_iterator (fds);
         while (gl_list_iterator_next (&iter, &element, NULL))
           {
-            int fd = (int) (uintptr_t) element;
-            close (fd);
+            asyncsafe_close ((struct closeable_fd *) element);
           }
         gl_list_iterator_free (&iter);
       }
@@ -294,8 +389,11 @@ create_temp_dir (const char *prefix, const char *parentdir,
             XNMALLOC (new_allocated, struct tempdir * volatile);
 
           if (old_allocated == 0)
-            /* First use of this facility.  Register the cleanup handler.  */
-            at_fatal_signal (&cleanup_action);
+            {
+              /* First use of this facility.  Register the cleanup handler.  */
+              init_fatal_signal_set ();
+              at_fatal_signal (&cleanup_action);
+            }
           else
             {
               /* Don't use memcpy() here, because memcpy takes non-volatile
@@ -650,30 +748,16 @@ register_fd (int fd)
   gl_lock_lock (descriptors_lock);
 
   if (descriptors == NULL)
-    descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL,
+    descriptors = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL,
                                         false);
-  gl_list_add_first (descriptors, (void *) (uintptr_t) fd);
 
-  gl_lock_unlock (descriptors_lock);
-}
+  struct closeable_fd *element = XMALLOC (struct closeable_fd);
+  element->fd = fd;
+  element->closed = false;
+  asyncsafe_spin_init (&element->lock);
+  element->done = false;
 
-/* Unregister a file descriptor to be closed.  */
-static void
-unregister_fd (int fd)
-{
-  gl_lock_lock (descriptors_lock);
-
-  gl_list_t fds = descriptors;
-  gl_list_node_t node;
-
-  if (fds == NULL)
-    /* descriptors should already contain fd.  */
-    abort ();
-  node = gl_list_search (fds, (void *) (uintptr_t) fd);
-  if (node == NULL)
-    /* descriptors should already contain fd.  */
-    abort ();
-  gl_list_remove_node (fds, node);
+  gl_list_add_first (descriptors, element);
 
   gl_lock_unlock (descriptors_lock);
 }
@@ -755,64 +839,141 @@ fopen_temp (const char *file_name, const char *mode, bool delete_on_close)
 int
 close_temp (int fd)
 {
-  if (fd >= 0)
-    {
-      /* No blocking of signals is needed here, since a double close of a
-         file descriptor is harmless.  */
-      int result = close (fd);
-      int saved_errno = errno;
+  if (fd < 0)
+    return close (fd);
 
-      /* No race condition here: we assume a single-threaded program, hence
-         fd cannot be re-opened here.  */
+  init_fatal_signal_set ();
 
-      unregister_fd (fd);
+  int result = 0;
+  int saved_errno = 0;
 
-      errno = saved_errno;
-      return result;
-    }
-  else
-    return close (fd);
+  gl_lock_lock (descriptors_lock);
+
+  gl_list_t list = descriptors;
+  if (list == NULL)
+    /* descriptors should already contain fd.  */
+    abort ();
+
+  /* Search through the list, and clean it up on the fly.  */
+  bool found = false;
+  gl_list_iterator_t iter = gl_list_iterator (list);
+  const void *elt;
+  gl_list_node_t node;
+  if (gl_list_iterator_next (&iter, &elt, &node))
+    for (;;)
+      {
+        struct closeable_fd *element = (struct closeable_fd *) elt;
+
+        /* Close the file descriptor, avoiding races with the signal
+           handler.  */
+        if (element->fd == fd)
+          {
+            result = asyncsafe_close (element);
+            saved_errno = errno;
+          }
+
+        bool free_this_node = element->done;
+        struct closeable_fd *element_to_free = element;
+        gl_list_node_t node_to_free = node;
+
+        bool have_next = gl_list_iterator_next (&iter, &elt, &node);
+
+        if (free_this_node)
+          {
+            free (element_to_free);
+            gl_list_remove_node (list, node_to_free);
+          }
+
+        if (!have_next)
+          break;
+      }
+  gl_list_iterator_free (&iter);
+  if (!found)
+    /* descriptors should already contain fd.  */
+    abort ();
+
+  gl_lock_unlock (descriptors_lock);
+
+  errno = saved_errno;
+  return result;
 }
 
-/* Close a temporary file in a temporary directory.
-   Unregisters the previously registered file descriptor.  */
-int
-fclose_temp (FILE *fp)
+static int
+fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *))
 {
   int fd = fileno (fp);
-  /* No blocking of signals is needed here, since a double close of a
-     file descriptor is harmless.  */
-  int result = fclose (fp);
-  int saved_errno = errno;
 
-  /* No race condition here: we assume a single-threaded program, hence
-     fd cannot be re-opened here.  */
+  init_fatal_signal_set ();
+
+  int result = 0;
+  int saved_errno = 0;
+
+  gl_lock_lock (descriptors_lock);
+
+  gl_list_t list = descriptors;
+  if (list == NULL)
+    /* descriptors should already contain fd.  */
+    abort ();
+
+  /* Search through the list, and clean it up on the fly.  */
+  bool found = false;
+  gl_list_iterator_t iter = gl_list_iterator (list);
+  const void *elt;
+  gl_list_node_t node;
+  if (gl_list_iterator_next (&iter, &elt, &node))
+    for (;;)
+      {
+        struct closeable_fd *element = (struct closeable_fd *) elt;
+
+        /* Close the file descriptor and the stream, avoiding races with the
+           signal handler.  */
+        if (element->fd == fd)
+          {
+            result = asyncsafe_fclose_variant (element, fp, fclose_variant);
+            saved_errno = errno;
+          }
+
+        bool free_this_node = element->done;
+        struct closeable_fd *element_to_free = element;
+        gl_list_node_t node_to_free = node;
 
-  unregister_fd (fd);
+        bool have_next = gl_list_iterator_next (&iter, &elt, &node);
+
+        if (free_this_node)
+          {
+            free (element_to_free);
+            gl_list_remove_node (list, node_to_free);
+          }
+
+        if (!have_next)
+          break;
+      }
+  gl_list_iterator_free (&iter);
+  if (!found)
+    /* descriptors should have contained fd.  */
+    abort ();
+
+  gl_lock_unlock (descriptors_lock);
 
   errno = saved_errno;
   return result;
 }
 
+/* Close a temporary file in a temporary directory.
+   Unregisters the previously registered file descriptor.  */
+int
+fclose_temp (FILE *fp)
+{
+  return fclose_variant_temp (fp, fclose);
+}
+
 #if GNULIB_FWRITEERROR
 /* Like fwriteerror.
    Unregisters the previously registered file descriptor.  */
 int
 fwriteerror_temp (FILE *fp)
 {
-  int fd = fileno (fp);
-  /* No blocking of signals is needed here, since a double close of a
-     file descriptor is harmless.  */
-  int result = fwriteerror (fp);
-  int saved_errno = errno;
-
-  /* No race condition here: we assume a single-threaded program, hence
-     fd cannot be re-opened here.  */
-
-  unregister_fd (fd);
-
-  errno = saved_errno;
-  return result;
+  return fclose_variant_temp (fp, fwriteerror);
 }
 #endif
 
@@ -822,18 +983,6 @@ fwriteerror_temp (FILE *fp)
 int
 close_stream_temp (FILE *fp)
 {
-  int fd = fileno (fp);
-  /* No blocking of signals is needed here, since a double close of a
-     file descriptor is harmless.  */
-  int result = close_stream (fp);
-  int saved_errno = errno;
-
-  /* No race condition here: we assume a single-threaded program, hence
-     fd cannot be re-opened here.  */
-
-  unregister_fd (fd);
-
-  errno = saved_errno;
-  return result;
+  return fclose_variant_temp (fp, close_stream);
 }
 #endif
diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c
index 8b27cc6..29184e0 100644
--- a/lib/fatal-signal.c
+++ b/lib/fatal-signal.c
@@ -346,3 +346,10 @@ get_fatal_signals (int signals[64])
     return p - signals;
   }
 }
+
+const sigset_t *
+get_fatal_signal_set (void)
+{
+  init_fatal_signal_set ();
+  return &fatal_signal_set;
+}
diff --git a/lib/fatal-signal.h b/lib/fatal-signal.h
index 2c0154f..54270fa 100644
--- a/lib/fatal-signal.h
+++ b/lib/fatal-signal.h
@@ -16,6 +16,11 @@
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 
+#ifndef _FATAL_SIGNAL_H
+#define _FATAL_SIGNAL_H
+
+#include <signal.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -77,7 +82,13 @@ extern void unblock_fatal_signals (void);
    Fills signals[0..count-1] and returns count.  */
 extern unsigned int get_fatal_signals (int signals[64]);
 
+/* Return the list of signals that block_fatal_signals/unblock_fatal_signals
+   would block or unblock.  */
+extern const sigset_t * get_fatal_signal_set (void);
+
 
 #ifdef __cplusplus
 }
 #endif
+
+#endif /* _FATAL_SIGNAL_H */
diff --git a/modules/clean-temp b/modules/clean-temp
index c1d5c1c..940869d 100644
--- a/modules/clean-temp
+++ b/modules/clean-temp
@@ -12,6 +12,7 @@ unistd
 lock
 error
 fatal-signal
+asyncsafe-spin
 open
 pathmax
 tmpdir
@@ -20,6 +21,7 @@ rmdir
 xalloc
 xmalloca
 linkedhash-list
+linked-list
 xlist
 gettext-h
 
-- 
2.7.4

Reply via email to