aboutsummaryrefslogtreecommitdiff
path: root/malloc/tst-malloc-fork-deadlock.c
diff options
context:
space:
mode:
Diffstat (limited to 'malloc/tst-malloc-fork-deadlock.c')
-rw-r--r--malloc/tst-malloc-fork-deadlock.c220
1 files changed, 220 insertions, 0 deletions
diff --git a/malloc/tst-malloc-fork-deadlock.c b/malloc/tst-malloc-fork-deadlock.c
new file mode 100644
index 0000000..94549ca
--- /dev/null
+++ b/malloc/tst-malloc-fork-deadlock.c
@@ -0,0 +1,220 @@
+/* Test concurrent fork, getline, and fflush (NULL).
+ Copyright (C) 2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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 2.1 of the
+ License, or (at your option) any later version.
+
+ The GNU C 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 the GNU C Library; see the file COPYING.LIB. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <time.h>
+#include <string.h>
+#include <signal.h>
+
+static int do_test (void);
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+enum {
+ /* Number of threads which call fork. */
+ fork_thread_count = 4,
+ /* Number of threads which call getline (and, indirectly,
+ malloc). */
+ read_thread_count = 8,
+};
+
+static bool termination_requested;
+
+static void *
+fork_thread_function (void *closure)
+{
+ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
+ {
+ pid_t pid = fork ();
+ if (pid < 0)
+ {
+ printf ("error: fork: %m\n");
+ abort ();
+ }
+ else if (pid == 0)
+ _exit (17);
+
+ int status;
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ printf ("error: waitpid: %m\n");
+ abort ();
+ }
+ if (!WIFEXITED (status) || WEXITSTATUS (status) != 17)
+ {
+ printf ("error: waitpid returned invalid status: %d\n", status);
+ abort ();
+ }
+ }
+ return NULL;
+}
+
+static char *file_to_read;
+
+static void *
+read_thread_function (void *closure)
+{
+ FILE *f = fopen (file_to_read, "r");
+ if (f == NULL)
+ {
+ printf ("error: fopen (%s): %m\n", file_to_read);
+ abort ();
+ }
+
+ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
+ {
+ rewind (f);
+ char *line = NULL;
+ size_t line_allocated = 0;
+ ssize_t ret = getline (&line, &line_allocated, f);
+ if (ret < 0)
+ {
+ printf ("error: getline: %m\n");
+ abort ();
+ }
+ free (line);
+ }
+ fclose (f);
+
+ return NULL;
+}
+
+static void *
+flushall_thread_function (void *closure)
+{
+ while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
+ if (fflush (NULL) != 0)
+ {
+ printf ("error: fflush (NULL): %m\n");
+ abort ();
+ }
+ return NULL;
+}
+
+static void
+create_threads (pthread_t *threads, size_t count, void *(*func) (void *))
+{
+ for (size_t i = 0; i < count; ++i)
+ {
+ int ret = pthread_create (threads + i, NULL, func, NULL);
+ if (ret != 0)
+ {
+ errno = ret;
+ printf ("error: pthread_create: %m\n");
+ abort ();
+ }
+ }
+}
+
+static void
+join_threads (pthread_t *threads, size_t count)
+{
+ for (size_t i = 0; i < count; ++i)
+ {
+ int ret = pthread_join (threads[i], NULL);
+ if (ret != 0)
+ {
+ errno = ret;
+ printf ("error: pthread_join: %m\n");
+ abort ();
+ }
+ }
+}
+
+/* Create a file which consists of a single long line, and assigns
+ file_to_read. The hope is that this triggers an allocation in
+ getline which needs a lock. */
+static void
+create_file_with_large_line (void)
+{
+ int fd = create_temp_file ("bug19431-large-line", &file_to_read);
+ if (fd < 0)
+ {
+ printf ("error: create_temp_file: %m\n");
+ abort ();
+ }
+ FILE *f = fdopen (fd, "w+");
+ if (f == NULL)
+ {
+ printf ("error: fdopen: %m\n");
+ abort ();
+ }
+ for (int i = 0; i < 50000; ++i)
+ fputc ('x', f);
+ fputc ('\n', f);
+ if (ferror (f))
+ {
+ printf ("error: fputc: %m\n");
+ abort ();
+ }
+ if (fclose (f) != 0)
+ {
+ printf ("error: fclose: %m\n");
+ abort ();
+ }
+}
+
+static int
+do_test (void)
+{
+ /* Make sure that we do not exceed the arena limit with the number
+ of threads we configured. */
+ if (mallopt (M_ARENA_MAX, 400) == 0)
+ {
+ printf ("error: mallopt (M_ARENA_MAX) failed\n");
+ return 1;
+ }
+
+ /* Leave some room for shutting down all threads gracefully. */
+ int timeout = 3;
+ if (timeout > TIMEOUT)
+ timeout = TIMEOUT - 1;
+
+ create_file_with_large_line ();
+
+ pthread_t fork_threads[fork_thread_count];
+ create_threads (fork_threads, fork_thread_count, fork_thread_function);
+ pthread_t read_threads[read_thread_count];
+ create_threads (read_threads, read_thread_count, read_thread_function);
+ pthread_t flushall_threads[1];
+ create_threads (flushall_threads, 1, flushall_thread_function);
+
+ struct timespec ts = {timeout, 0};
+ if (nanosleep (&ts, NULL))
+ {
+ printf ("error: error: nanosleep: %m\n");
+ abort ();
+ }
+
+ __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
+
+ join_threads (flushall_threads, 1);
+ join_threads (read_threads, read_thread_count);
+ join_threads (fork_threads, fork_thread_count);
+
+ free (file_to_read);
+
+ return 0;
+}