|
|
147e83 |
Backport of these upstream commits:
|
|
|
147e83 |
|
|
|
147e83 |
commit 29d794863cd6e03115d3670707cc873a9965ba92
|
|
|
147e83 |
Author: Florian Weimer <fweimer@redhat.com>
|
|
|
147e83 |
Date: Thu Apr 14 09:17:02 2016 +0200
|
|
|
147e83 |
|
|
|
147e83 |
malloc: Run fork handler as late as possible [BZ #19431]
|
|
|
147e83 |
|
|
|
147e83 |
Previously, a thread M invoking fork would acquire locks in this order:
|
|
|
147e83 |
|
|
|
147e83 |
(M1) malloc arena locks (in the registered fork handler)
|
|
|
147e83 |
(M2) libio list lock
|
|
|
147e83 |
|
|
|
147e83 |
A thread F invoking flush (NULL) would acquire locks in this order:
|
|
|
147e83 |
|
|
|
147e83 |
(F1) libio list lock
|
|
|
147e83 |
(F2) individual _IO_FILE locks
|
|
|
147e83 |
|
|
|
147e83 |
A thread G running getdelim would use this order:
|
|
|
147e83 |
|
|
|
147e83 |
(G1) _IO_FILE lock
|
|
|
147e83 |
(G2) malloc arena lock
|
|
|
147e83 |
|
|
|
147e83 |
After executing (M1), (F1), (G1), none of the threads can make progress.
|
|
|
147e83 |
|
|
|
147e83 |
This commit changes the fork lock order to:
|
|
|
147e83 |
|
|
|
147e83 |
(M'1) libio list lock
|
|
|
147e83 |
(M'2) malloc arena locks
|
|
|
147e83 |
|
|
|
147e83 |
It explicitly encodes the lock order in the implementations of fork,
|
|
|
147e83 |
and does not rely on the registration order, thus avoiding the deadlock.
|
|
|
147e83 |
|
|
|
147e83 |
commit 186fe877f3df0b84d57dfbf0386f6332c6aa69bc
|
|
|
147e83 |
Author: Florian Weimer <fweimer@redhat.com>
|
|
|
147e83 |
Date: Thu Apr 14 12:53:03 2016 +0200
|
|
|
147e83 |
|
|
|
147e83 |
malloc: Add missing internal_function attributes on function definitions
|
|
|
147e83 |
|
|
|
147e83 |
Fixes build on i386 after commit 29d794863cd6e03115d3670707cc873a9965ba92.
|
|
|
147e83 |
|
|
|
147e83 |
Index: b/malloc/Makefile
|
|
|
147e83 |
===================================================================
|
|
|
147e83 |
--- a/malloc/Makefile
|
|
|
147e83 |
+++ b/malloc/Makefile
|
|
|
147e83 |
@@ -28,7 +28,7 @@ tests := mallocbug tst-malloc tst-valloc
|
|
|
147e83 |
tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \
|
|
|
147e83 |
tst-malloc-usable \
|
|
|
147e83 |
tst-malloc-backtrace tst-malloc-thread-exit \
|
|
|
147e83 |
- tst-malloc-thread-fail
|
|
|
147e83 |
+ tst-malloc-thread-fail tst-malloc-fork-deadlock
|
|
|
147e83 |
test-srcs = tst-mtrace
|
|
|
147e83 |
|
|
|
147e83 |
routines = malloc morecore mcheck mtrace obstack
|
|
|
147e83 |
@@ -49,6 +49,7 @@ $(objpfx)tst-malloc-thread-fail: $(commo
|
|
|
147e83 |
$(common-objpfx)nptl/libpthread_nonshared.a
|
|
|
147e83 |
$(objpfx)tst-malloc-thread-exit: $(common-objpfx)nptl/libpthread.so \
|
|
|
147e83 |
$(common-objpfx)nptl/libpthread_nonshared.a
|
|
|
147e83 |
+$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
|
|
|
147e83 |
|
|
|
147e83 |
# These should be removed by `make clean'.
|
|
|
147e83 |
extra-objs = mcheck-init.o libmcheck.a
|
|
|
147e83 |
Index: b/malloc/arena.c
|
|
|
147e83 |
===================================================================
|
|
|
147e83 |
--- a/malloc/arena.c
|
|
|
147e83 |
+++ b/malloc/arena.c
|
|
|
147e83 |
@@ -162,10 +162,6 @@ static void (*save_free_hook)
|
|
|
147e83 |
const __malloc_ptr_t);
|
|
|
147e83 |
static void* save_arena;
|
|
|
147e83 |
|
|
|
147e83 |
-#ifdef ATFORK_MEM
|
|
|
147e83 |
-ATFORK_MEM;
|
|
|
147e83 |
-#endif
|
|
|
147e83 |
-
|
|
|
147e83 |
/* Magic value for the thread-specific arena pointer when
|
|
|
147e83 |
malloc_atfork() is in use. */
|
|
|
147e83 |
|
|
|
147e83 |
@@ -228,14 +224,15 @@ free_atfork(void* mem, const void *calle
|
|
|
147e83 |
/* Counter for number of times the list is locked by the same thread. */
|
|
|
147e83 |
static unsigned int atfork_recursive_cntr;
|
|
|
147e83 |
|
|
|
147e83 |
-/* The following two functions are registered via thread_atfork() to
|
|
|
147e83 |
- make sure that the mutexes remain in a consistent state in the
|
|
|
147e83 |
- fork()ed version of a thread. Also adapt the malloc and free hooks
|
|
|
147e83 |
- temporarily, because the `atfork' handler mechanism may use
|
|
|
147e83 |
- malloc/free internally (e.g. in LinuxThreads). */
|
|
|
147e83 |
+/* The following three functions are called around fork from a
|
|
|
147e83 |
+ multi-threaded process. We do not use the general fork handler
|
|
|
147e83 |
+ mechanism to make sure that our handlers are the last ones being
|
|
|
147e83 |
+ called, so that other fork handlers can use the malloc
|
|
|
147e83 |
+ subsystem. */
|
|
|
147e83 |
|
|
|
147e83 |
-static void
|
|
|
147e83 |
-ptmalloc_lock_all (void)
|
|
|
147e83 |
+void
|
|
|
147e83 |
+internal_function
|
|
|
147e83 |
+__malloc_fork_lock_parent (void)
|
|
|
147e83 |
{
|
|
|
147e83 |
mstate ar_ptr;
|
|
|
147e83 |
|
|
|
147e83 |
@@ -243,7 +240,7 @@ ptmalloc_lock_all (void)
|
|
|
147e83 |
return;
|
|
|
147e83 |
|
|
|
147e83 |
/* We do not acquire free_list_lock here because we completely
|
|
|
147e83 |
- reconstruct free_list in ptmalloc_unlock_all2. */
|
|
|
147e83 |
+ reconstruct free_list in __malloc_fork_unlock_child. */
|
|
|
147e83 |
|
|
|
147e83 |
if (mutex_trylock(&list_lock))
|
|
|
147e83 |
{
|
|
|
147e83 |
@@ -268,7 +265,7 @@ ptmalloc_lock_all (void)
|
|
|
147e83 |
__free_hook = free_atfork;
|
|
|
147e83 |
/* Only the current thread may perform malloc/free calls now.
|
|
|
147e83 |
save_arena will be reattached to the current thread, in
|
|
|
147e83 |
- ptmalloc_lock_all, so save_arena->attached_threads is not
|
|
|
147e83 |
+ __malloc_fork_lock_parent, so save_arena->attached_threads is not
|
|
|
147e83 |
updated. */
|
|
|
147e83 |
tsd_getspecific(arena_key, save_arena);
|
|
|
147e83 |
tsd_setspecific(arena_key, ATFORK_ARENA_PTR);
|
|
|
147e83 |
@@ -276,8 +273,9 @@ ptmalloc_lock_all (void)
|
|
|
147e83 |
++atfork_recursive_cntr;
|
|
|
147e83 |
}
|
|
|
147e83 |
|
|
|
147e83 |
-static void
|
|
|
147e83 |
-ptmalloc_unlock_all (void)
|
|
|
147e83 |
+void
|
|
|
147e83 |
+internal_function
|
|
|
147e83 |
+__malloc_fork_unlock_parent (void)
|
|
|
147e83 |
{
|
|
|
147e83 |
mstate ar_ptr;
|
|
|
147e83 |
|
|
|
147e83 |
@@ -286,8 +284,8 @@ ptmalloc_unlock_all (void)
|
|
|
147e83 |
if (--atfork_recursive_cntr != 0)
|
|
|
147e83 |
return;
|
|
|
147e83 |
/* Replace ATFORK_ARENA_PTR with save_arena.
|
|
|
147e83 |
- save_arena->attached_threads was not changed in ptmalloc_lock_all
|
|
|
147e83 |
- and is still correct. */
|
|
|
147e83 |
+ save_arena->attached_threads was not changed in
|
|
|
147e83 |
+ __malloc_fork_lock_parent and is still correct. */
|
|
|
147e83 |
tsd_setspecific(arena_key, save_arena);
|
|
|
147e83 |
__malloc_hook = save_malloc_hook;
|
|
|
147e83 |
__free_hook = save_free_hook;
|
|
|
147e83 |
@@ -299,15 +297,9 @@ ptmalloc_unlock_all (void)
|
|
|
147e83 |
(void)mutex_unlock(&list_lock);
|
|
|
147e83 |
}
|
|
|
147e83 |
|
|
|
147e83 |
-# ifdef __linux__
|
|
|
147e83 |
-
|
|
|
147e83 |
-/* In NPTL, unlocking a mutex in the child process after a
|
|
|
147e83 |
- fork() is currently unsafe, whereas re-initializing it is safe and
|
|
|
147e83 |
- does not leak resources. Therefore, a special atfork handler is
|
|
|
147e83 |
- installed for the child. */
|
|
|
147e83 |
-
|
|
|
147e83 |
-static void
|
|
|
147e83 |
-ptmalloc_unlock_all2 (void)
|
|
|
147e83 |
+void
|
|
|
147e83 |
+internal_function
|
|
|
147e83 |
+__malloc_fork_unlock_child (void)
|
|
|
147e83 |
{
|
|
|
147e83 |
mstate ar_ptr;
|
|
|
147e83 |
|
|
|
147e83 |
@@ -338,12 +330,6 @@ ptmalloc_unlock_all2 (void)
|
|
|
147e83 |
atfork_recursive_cntr = 0;
|
|
|
147e83 |
}
|
|
|
147e83 |
|
|
|
147e83 |
-# else
|
|
|
147e83 |
-
|
|
|
147e83 |
-# define ptmalloc_unlock_all2 ptmalloc_unlock_all
|
|
|
147e83 |
-
|
|
|
147e83 |
-# endif
|
|
|
147e83 |
-
|
|
|
147e83 |
#endif /* !NO_THREADS */
|
|
|
147e83 |
|
|
|
147e83 |
/* Initialization routine. */
|
|
|
147e83 |
@@ -413,7 +399,6 @@ ptmalloc_init (void)
|
|
|
147e83 |
|
|
|
147e83 |
tsd_key_create(&arena_key, NULL);
|
|
|
147e83 |
tsd_setspecific(arena_key, (void *)&main_arena);
|
|
|
147e83 |
- thread_atfork(ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
|
|
|
147e83 |
const char *s = NULL;
|
|
|
147e83 |
if (__builtin_expect (_environ != NULL, 1))
|
|
|
147e83 |
{
|
|
|
147e83 |
@@ -487,12 +472,6 @@ ptmalloc_init (void)
|
|
|
147e83 |
__malloc_initialized = 1;
|
|
|
147e83 |
}
|
|
|
147e83 |
|
|
|
147e83 |
-/* There are platforms (e.g. Hurd) with a link-time hook mechanism. */
|
|
|
147e83 |
-#ifdef thread_atfork_static
|
|
|
147e83 |
-thread_atfork_static(ptmalloc_lock_all, ptmalloc_unlock_all, \
|
|
|
147e83 |
- ptmalloc_unlock_all2)
|
|
|
147e83 |
-#endif
|
|
|
147e83 |
-
|
|
|
147e83 |
|
|
|
147e83 |
|
|
|
147e83 |
/* Managing heaps and arenas (for concurrent threads) */
|
|
|
147e83 |
@@ -827,7 +806,8 @@ _int_new_arena(size_t size)
|
|
|
147e83 |
limit is reached). At this point, some arena has to be attached
|
|
|
147e83 |
to two threads. We could acquire the arena lock before list_lock
|
|
|
147e83 |
to make it less likely that reused_arena picks this new arena,
|
|
|
147e83 |
- but this could result in a deadlock with ptmalloc_lock_all. */
|
|
|
147e83 |
+ but this could result in a deadlock with
|
|
|
147e83 |
+ __malloc_fork_lock_parent. */
|
|
|
147e83 |
|
|
|
147e83 |
(void) mutex_lock (&a->mutex);
|
|
|
147e83 |
|
|
|
147e83 |
Index: b/malloc/malloc-internal.h
|
|
|
147e83 |
===================================================================
|
|
|
147e83 |
--- /dev/null
|
|
|
147e83 |
+++ b/malloc/malloc-internal.h
|
|
|
147e83 |
@@ -0,0 +1,32 @@
|
|
|
147e83 |
+/* Internal declarations for malloc, for use within libc.
|
|
|
147e83 |
+ Copyright (C) 2016 Free Software Foundation, Inc.
|
|
|
147e83 |
+ This file is part of the GNU C Library.
|
|
|
147e83 |
+
|
|
|
147e83 |
+ The GNU C Library is free software; you can redistribute it and/or
|
|
|
147e83 |
+ modify it under the terms of the GNU Lesser General Public License as
|
|
|
147e83 |
+ published by the Free Software Foundation; either version 2.1 of the
|
|
|
147e83 |
+ License, or (at your option) any later version.
|
|
|
147e83 |
+
|
|
|
147e83 |
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
|
147e83 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
147e83 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
147e83 |
+ Lesser General Public License for more details.
|
|
|
147e83 |
+
|
|
|
147e83 |
+ You should have received a copy of the GNU Lesser General Public
|
|
|
147e83 |
+ License along with the GNU C Library; see the file COPYING.LIB. If
|
|
|
147e83 |
+ not, see <http://www.gnu.org/licenses/>. */
|
|
|
147e83 |
+
|
|
|
147e83 |
+#ifndef _MALLOC_PRIVATE_H
|
|
|
147e83 |
+#define _MALLOC_PRIVATE_H
|
|
|
147e83 |
+
|
|
|
147e83 |
+/* Called in the parent process before a fork. */
|
|
|
147e83 |
+void __malloc_fork_lock_parent (void) internal_function attribute_hidden;
|
|
|
147e83 |
+
|
|
|
147e83 |
+/* Called in the parent process after a fork. */
|
|
|
147e83 |
+void __malloc_fork_unlock_parent (void) internal_function attribute_hidden;
|
|
|
147e83 |
+
|
|
|
147e83 |
+/* Called in the child process after a fork. */
|
|
|
147e83 |
+void __malloc_fork_unlock_child (void) internal_function attribute_hidden;
|
|
|
147e83 |
+
|
|
|
147e83 |
+
|
|
|
147e83 |
+#endif /* _MALLOC_PRIVATE_H */
|
|
|
147e83 |
Index: b/malloc/malloc.c
|
|
|
147e83 |
===================================================================
|
|
|
147e83 |
--- a/malloc/malloc.c
|
|
|
147e83 |
+++ b/malloc/malloc.c
|
|
|
147e83 |
@@ -291,6 +291,7 @@ __malloc_assert (const char *assertion,
|
|
|
147e83 |
}
|
|
|
147e83 |
#endif
|
|
|
147e83 |
|
|
|
147e83 |
+#include <malloc/malloc-internal.h>
|
|
|
147e83 |
|
|
|
147e83 |
/*
|
|
|
147e83 |
INTERNAL_SIZE_T is the word-size used for internal bookkeeping
|
|
|
147e83 |
Index: b/malloc/tst-malloc-fork-deadlock.c
|
|
|
147e83 |
===================================================================
|
|
|
147e83 |
--- /dev/null
|
|
|
147e83 |
+++ b/malloc/tst-malloc-fork-deadlock.c
|
|
|
147e83 |
@@ -0,0 +1,220 @@
|
|
|
147e83 |
+/* Test concurrent fork, getline, and fflush (NULL).
|
|
|
147e83 |
+ Copyright (C) 2016 Free Software Foundation, Inc.
|
|
|
147e83 |
+ This file is part of the GNU C Library.
|
|
|
147e83 |
+
|
|
|
147e83 |
+ The GNU C Library is free software; you can redistribute it and/or
|
|
|
147e83 |
+ modify it under the terms of the GNU Lesser General Public License as
|
|
|
147e83 |
+ published by the Free Software Foundation; either version 2.1 of the
|
|
|
147e83 |
+ License, or (at your option) any later version.
|
|
|
147e83 |
+
|
|
|
147e83 |
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
|
147e83 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
147e83 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
147e83 |
+ Lesser General Public License for more details.
|
|
|
147e83 |
+
|
|
|
147e83 |
+ You should have received a copy of the GNU Lesser General Public
|
|
|
147e83 |
+ License along with the GNU C Library; see the file COPYING.LIB. If
|
|
|
147e83 |
+ not, see <http://www.gnu.org/licenses/>. */
|
|
|
147e83 |
+
|
|
|
147e83 |
+#include <sys/wait.h>
|
|
|
147e83 |
+#include <unistd.h>
|
|
|
147e83 |
+#include <errno.h>
|
|
|
147e83 |
+#include <stdio.h>
|
|
|
147e83 |
+#include <pthread.h>
|
|
|
147e83 |
+#include <stdbool.h>
|
|
|
147e83 |
+#include <stdlib.h>
|
|
|
147e83 |
+#include <malloc.h>
|
|
|
147e83 |
+#include <time.h>
|
|
|
147e83 |
+#include <string.h>
|
|
|
147e83 |
+#include <signal.h>
|
|
|
147e83 |
+
|
|
|
147e83 |
+static int do_test (void);
|
|
|
147e83 |
+#define TEST_FUNCTION do_test ()
|
|
|
147e83 |
+#include "../test-skeleton.c"
|
|
|
147e83 |
+
|
|
|
147e83 |
+enum {
|
|
|
147e83 |
+ /* Number of threads which call fork. */
|
|
|
147e83 |
+ fork_thread_count = 4,
|
|
|
147e83 |
+ /* Number of threads which call getline (and, indirectly,
|
|
|
147e83 |
+ malloc). */
|
|
|
147e83 |
+ read_thread_count = 8,
|
|
|
147e83 |
+};
|
|
|
147e83 |
+
|
|
|
147e83 |
+static bool termination_requested;
|
|
|
147e83 |
+
|
|
|
147e83 |
+static void *
|
|
|
147e83 |
+fork_thread_function (void *closure)
|
|
|
147e83 |
+{
|
|
|
147e83 |
+ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ pid_t pid = fork ();
|
|
|
147e83 |
+ if (pid < 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: fork: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ else if (pid == 0)
|
|
|
147e83 |
+ _exit (17);
|
|
|
147e83 |
+
|
|
|
147e83 |
+ int status;
|
|
|
147e83 |
+ if (waitpid (pid, &status, 0) < 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: waitpid: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ if (!WIFEXITED (status) || WEXITSTATUS (status) != 17)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: waitpid returned invalid status: %d\n", status);
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ return NULL;
|
|
|
147e83 |
+}
|
|
|
147e83 |
+
|
|
|
147e83 |
+static char *file_to_read;
|
|
|
147e83 |
+
|
|
|
147e83 |
+static void *
|
|
|
147e83 |
+read_thread_function (void *closure)
|
|
|
147e83 |
+{
|
|
|
147e83 |
+ FILE *f = fopen (file_to_read, "r");
|
|
|
147e83 |
+ if (f == NULL)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: fopen (%s): %m\n", file_to_read);
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+
|
|
|
147e83 |
+ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ rewind (f);
|
|
|
147e83 |
+ char *line = NULL;
|
|
|
147e83 |
+ size_t line_allocated = 0;
|
|
|
147e83 |
+ ssize_t ret = getline (&line, &line_allocated, f);
|
|
|
147e83 |
+ if (ret < 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: getline: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ free (line);
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ fclose (f);
|
|
|
147e83 |
+
|
|
|
147e83 |
+ return NULL;
|
|
|
147e83 |
+}
|
|
|
147e83 |
+
|
|
|
147e83 |
+static void *
|
|
|
147e83 |
+flushall_thread_function (void *closure)
|
|
|
147e83 |
+{
|
|
|
147e83 |
+ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
|
|
|
147e83 |
+ if (fflush (NULL) != 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: fflush (NULL): %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ return NULL;
|
|
|
147e83 |
+}
|
|
|
147e83 |
+
|
|
|
147e83 |
+static void
|
|
|
147e83 |
+create_threads (pthread_t *threads, size_t count, void *(*func) (void *))
|
|
|
147e83 |
+{
|
|
|
147e83 |
+ for (size_t i = 0; i < count; ++i)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ int ret = pthread_create (threads + i, NULL, func, NULL);
|
|
|
147e83 |
+ if (ret != 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ errno = ret;
|
|
|
147e83 |
+ printf ("error: pthread_create: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+}
|
|
|
147e83 |
+
|
|
|
147e83 |
+static void
|
|
|
147e83 |
+join_threads (pthread_t *threads, size_t count)
|
|
|
147e83 |
+{
|
|
|
147e83 |
+ for (size_t i = 0; i < count; ++i)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ int ret = pthread_join (threads[i], NULL);
|
|
|
147e83 |
+ if (ret != 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ errno = ret;
|
|
|
147e83 |
+ printf ("error: pthread_join: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+}
|
|
|
147e83 |
+
|
|
|
147e83 |
+/* Create a file which consists of a single long line, and assigns
|
|
|
147e83 |
+ file_to_read. The hope is that this triggers an allocation in
|
|
|
147e83 |
+ getline which needs a lock. */
|
|
|
147e83 |
+static void
|
|
|
147e83 |
+create_file_with_large_line (void)
|
|
|
147e83 |
+{
|
|
|
147e83 |
+ int fd = create_temp_file ("bug19431-large-line", &file_to_read);
|
|
|
147e83 |
+ if (fd < 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: create_temp_file: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ FILE *f = fdopen (fd, "w+");
|
|
|
147e83 |
+ if (f == NULL)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: fdopen: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ for (int i = 0; i < 50000; ++i)
|
|
|
147e83 |
+ fputc ('x', f);
|
|
|
147e83 |
+ fputc ('\n', f);
|
|
|
147e83 |
+ if (ferror (f))
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: fputc: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+ if (fclose (f) != 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: fclose: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+}
|
|
|
147e83 |
+
|
|
|
147e83 |
+static int
|
|
|
147e83 |
+do_test (void)
|
|
|
147e83 |
+{
|
|
|
147e83 |
+ /* Make sure that we do not exceed the arena limit with the number
|
|
|
147e83 |
+ of threads we configured. */
|
|
|
147e83 |
+ if (mallopt (M_ARENA_MAX, 400) == 0)
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: mallopt (M_ARENA_MAX) failed\n");
|
|
|
147e83 |
+ return 1;
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+
|
|
|
147e83 |
+ /* Leave some room for shutting down all threads gracefully. */
|
|
|
147e83 |
+ int timeout = 3;
|
|
|
147e83 |
+ if (timeout > TIMEOUT)
|
|
|
147e83 |
+ timeout = TIMEOUT - 1;
|
|
|
147e83 |
+
|
|
|
147e83 |
+ create_file_with_large_line ();
|
|
|
147e83 |
+
|
|
|
147e83 |
+ pthread_t fork_threads[fork_thread_count];
|
|
|
147e83 |
+ create_threads (fork_threads, fork_thread_count, fork_thread_function);
|
|
|
147e83 |
+ pthread_t read_threads[read_thread_count];
|
|
|
147e83 |
+ create_threads (read_threads, read_thread_count, read_thread_function);
|
|
|
147e83 |
+ pthread_t flushall_threads[1];
|
|
|
147e83 |
+ create_threads (flushall_threads, 1, flushall_thread_function);
|
|
|
147e83 |
+
|
|
|
147e83 |
+ struct timespec ts = {timeout, 0};
|
|
|
147e83 |
+ if (nanosleep (&ts, NULL))
|
|
|
147e83 |
+ {
|
|
|
147e83 |
+ printf ("error: error: nanosleep: %m\n");
|
|
|
147e83 |
+ abort ();
|
|
|
147e83 |
+ }
|
|
|
147e83 |
+
|
|
|
147e83 |
+ __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
|
|
|
147e83 |
+
|
|
|
147e83 |
+ join_threads (flushall_threads, 1);
|
|
|
147e83 |
+ join_threads (read_threads, read_thread_count);
|
|
|
147e83 |
+ join_threads (fork_threads, fork_thread_count);
|
|
|
147e83 |
+
|
|
|
147e83 |
+ free (file_to_read);
|
|
|
147e83 |
+
|
|
|
147e83 |
+ return 0;
|
|
|
147e83 |
+}
|
|
|
147e83 |
Index: b/manual/memory.texi
|
|
|
147e83 |
===================================================================
|
|
|
147e83 |
--- a/manual/memory.texi
|
|
|
147e83 |
+++ b/manual/memory.texi
|
|
|
147e83 |
@@ -1055,14 +1055,6 @@ systems that do not support @w{ISO C11}.
|
|
|
147e83 |
@c _dl_addr_inside_object ok
|
|
|
147e83 |
@c determine_info ok
|
|
|
147e83 |
@c __rtld_lock_unlock_recursive (dl_load_lock) @aculock
|
|
|
147e83 |
-@c thread_atfork @asulock @aculock @acsfd @acsmem
|
|
|
147e83 |
-@c __register_atfork @asulock @aculock @acsfd @acsmem
|
|
|
147e83 |
-@c lll_lock (__fork_lock) @asulock @aculock
|
|
|
147e83 |
-@c fork_handler_alloc @asulock @aculock @acsfd @acsmem
|
|
|
147e83 |
-@c calloc dup @asulock @aculock @acsfd @acsmem
|
|
|
147e83 |
-@c __linkin_atfork ok
|
|
|
147e83 |
-@c catomic_compare_and_exchange_bool_acq ok
|
|
|
147e83 |
-@c lll_unlock (__fork_lock) @aculock
|
|
|
147e83 |
@c *_environ @mtsenv
|
|
|
147e83 |
@c next_env_entry ok
|
|
|
147e83 |
@c strcspn dup ok
|
|
|
147e83 |
Index: b/nptl/sysdeps/unix/sysv/linux/fork.c
|
|
|
147e83 |
===================================================================
|
|
|
147e83 |
--- a/nptl/sysdeps/unix/sysv/linux/fork.c
|
|
|
147e83 |
+++ b/nptl/sysdeps/unix/sysv/linux/fork.c
|
|
|
147e83 |
@@ -29,7 +29,7 @@
|
|
|
147e83 |
#include <bits/stdio-lock.h>
|
|
|
147e83 |
#include <atomic.h>
|
|
|
147e83 |
#include <pthreadP.h>
|
|
|
147e83 |
-
|
|
|
147e83 |
+#include <malloc/malloc-internal.h>
|
|
|
147e83 |
|
|
|
147e83 |
unsigned long int *__fork_generation_pointer;
|
|
|
147e83 |
|
|
|
147e83 |
@@ -116,6 +116,11 @@ __libc_fork (void)
|
|
|
147e83 |
|
|
|
147e83 |
_IO_list_lock ();
|
|
|
147e83 |
|
|
|
147e83 |
+ /* Acquire malloc locks. This needs to come last because fork
|
|
|
147e83 |
+ handlers may use malloc, and the libio list lock has an indirect
|
|
|
147e83 |
+ malloc dependency as well (via the getdelim function). */
|
|
|
147e83 |
+ __malloc_fork_lock_parent ();
|
|
|
147e83 |
+
|
|
|
147e83 |
#ifndef NDEBUG
|
|
|
147e83 |
pid_t ppid = THREAD_GETMEM (THREAD_SELF, tid);
|
|
|
147e83 |
#endif
|
|
|
147e83 |
@@ -172,6 +177,9 @@ __libc_fork (void)
|
|
|
147e83 |
# endif
|
|
|
147e83 |
#endif
|
|
|
147e83 |
|
|
|
147e83 |
+ /* Release malloc locks. */
|
|
|
147e83 |
+ __malloc_fork_unlock_child ();
|
|
|
147e83 |
+
|
|
|
147e83 |
/* Reset the file list. These are recursive mutexes. */
|
|
|
147e83 |
fresetlockfiles ();
|
|
|
147e83 |
|
|
|
147e83 |
@@ -213,6 +221,9 @@ __libc_fork (void)
|
|
|
147e83 |
/* Restore the PID value. */
|
|
|
147e83 |
THREAD_SETMEM (THREAD_SELF, pid, parentpid);
|
|
|
147e83 |
|
|
|
147e83 |
+ /* Release malloc locks, parent process variant. */
|
|
|
147e83 |
+ __malloc_fork_unlock_parent ();
|
|
|
147e83 |
+
|
|
|
147e83 |
/* We execute this even if the 'fork' call failed. */
|
|
|
147e83 |
_IO_list_unlock ();
|
|
|
147e83 |
|