From c0f3519d2e841dbaad086091f610cebc8178e7f6 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 28 May 2000 22:14:55 +0000 Subject: Update. * posix/Makefile (tests): Add tst-fork. --- ChangeLog | 2 + linuxthreads/ChangeLog | 20 ++++++ linuxthreads/Examples/ex8.c | 101 ++++++++++++++++++++++++++++++ linuxthreads/Makefile | 3 +- linuxthreads/linuxthreads.texi | 14 +++++ linuxthreads/ptfork.c | 20 +++--- posix/Makefile | 2 +- posix/tst-fork.c | 136 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 288 insertions(+), 10 deletions(-) create mode 100644 linuxthreads/Examples/ex8.c create mode 100644 posix/tst-fork.c diff --git a/ChangeLog b/ChangeLog index e161921ff1..b23ac997e0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2000-05-28 Ulrich Drepper + * posix/Makefile (tests): Add tst-fork. + * resolv/inet_pton.c: Loose __P. 2000-05-21 Jakub Jelinek diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog index 4bb1a81d17..38cbe5879b 100644 --- a/linuxthreads/ChangeLog +++ b/linuxthreads/ChangeLog @@ -1,3 +1,23 @@ +2000-05-28 Ulrich Drepper + + * Makefile (tests): Add ex8. + * Examples/ex8.c: New file. + +2000-05-12 Kaz Kylheku + + Bugfix: The pthread_atfork mechanism now takes care of its + own internal mutex at fork time. + + * ptfork.c (__fork): Revised so that the mutex is held across + the fork operation and while the handlers are called, and so that + the child resets the mutex. + Added calls to new functions __flockallfiles and __funlockallfiles. + + * linuxthreads.texi: Updated pthread_atfork documentation to make + it clear that fork and pthread_atfork can't be reentered from + atfork handlers, that pthread_atfork and fork are mutually atomic, + and that the handlers are inherited by the child process. + 2000-05-24 Ulrich Drepper * Makefile (libpthread-routines): Add pspinlock. diff --git a/linuxthreads/Examples/ex8.c b/linuxthreads/Examples/ex8.c new file mode 100644 index 0000000000..89ad8ae5d5 --- /dev/null +++ b/linuxthreads/Examples/ex8.c @@ -0,0 +1,101 @@ +/* Tests for fork in multi-threaded environment. + Copyright (C) 2000 Free Software Foundation, Inc. + Contributed by Ulrich Drepper , 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include + +enum +{ + PREPARE_BIT = 1, + PARENT_BIT = 2, + CHILD_BIT = 4 +}; + +static int var; + +static void +prepare (void) +{ + var |= PREPARE_BIT; +} + +static void +parent (void) +{ + var |= PARENT_BIT; +} + +static void +child (void) +{ + var |= CHILD_BIT; +} + + +static void *thread (void *arg); + + +int +main (void) +{ + pthread_t th; + void *res; + + pthread_atfork (prepare, parent, child); + + if (pthread_create (&th, NULL, thread, NULL) != 0) + error (EXIT_FAILURE, 0, "cannot create thread"); + + pthread_join (th, &res); + + return (int) res; +} + + +static void * +thread (void *arg) +{ + int status; + pid_t pid; + + pid = fork (); + if (pid == 0) + { + /* We check whether the `prepare' and `child' function ran. */ + exit (var != (PREPARE_BIT | CHILD_BIT)); + } + else if (pid == (pid_t) -1) + error (EXIT_FAILURE, errno, "cannot fork"); + + if (waitpid (pid, &status, 0) != pid) + error (EXIT_FAILURE, errno, "wrong child"); + + if (WTERMSIG (status) != 0) + error (EXIT_FAILURE, 0, "Child terminated incorrectly"); + status = WEXITSTATUS (status); + + if (status == 0) + status = var != (PREPARE_BIT | PARENT_BIT); + + return (void *) status; +} diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile index 777fb2b408..afbe51c618 100644 --- a/linuxthreads/Makefile +++ b/linuxthreads/Makefile @@ -38,7 +38,7 @@ libpthread-routines := attr cancel condvar join manager mutex ptfork \ oldsemaphore events getcpuclockid pspinlock vpath %.c Examples -tests = ex1 ex2 ex3 ex4 ex5 ex6 ex7 +tests = ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 include ../Rules @@ -67,3 +67,4 @@ $(objpfx)ex4: $(libpthread) $(objpfx)ex5: $(libpthread) $(objpfx)ex6: $(libpthread) $(objpfx)ex7: $(libpthread) +$(objpfx)ex8: $(libpthread) diff --git a/linuxthreads/linuxthreads.texi b/linuxthreads/linuxthreads.texi index 2b3647aed0..f50199e9fc 100644 --- a/linuxthreads/linuxthreads.texi +++ b/linuxthreads/linuxthreads.texi @@ -1314,6 +1314,20 @@ handlers are called in FIFO order (first added, first called). If there is insufficient memory available to register the handlers, @code{pthread_atfork} fails and returns @code{ENOMEM}. Otherwise it returns 0. + +The functions @code{fork} and @code{pthread_atfork} must not be regarded as +reentrant from the context of the handlers. That is to say, if a +@code{pthread_atfork} handler invoked from within @code{fork} calls +@code{pthread_atfork} or @code{fork}, the behavior is undefined. + +Registering a triplet of handlers is an atomic operation with respect to fork. +If new handlers are registered at about the same time as a fork occurs, either +all three handlers will be called, or none of them will be called. + +The handlers are inherited by the child process, and there is no +way to remove them, short of using @code{exec} to load a new +pocess image. + @end deftypefun To understand the purpose of @code{pthread_atfork}, recall that diff --git a/linuxthreads/ptfork.c b/linuxthreads/ptfork.c index cb6d46db8c..440d66c4a3 100644 --- a/linuxthreads/ptfork.c +++ b/linuxthreads/ptfork.c @@ -78,25 +78,29 @@ extern int __libc_fork(void); pid_t __fork(void) { pid_t pid; - struct handler_list * prepare, * child, * parent; pthread_mutex_lock(&pthread_atfork_lock); - prepare = pthread_atfork_prepare; - child = pthread_atfork_child; - parent = pthread_atfork_parent; - pthread_mutex_unlock(&pthread_atfork_lock); - pthread_call_handlers(prepare); + + pthread_call_handlers(pthread_atfork_prepare); __pthread_once_fork_prepare(); + pid = __libc_fork(); + if (pid == 0) { __pthread_reset_main_thread(); + __fresetlockfiles(); - pthread_call_handlers(child); __pthread_once_fork_child(); + pthread_call_handlers(pthread_atfork_child); + + pthread_mutex_init(&pthread_atfork_lock, NULL); } else { - pthread_call_handlers(parent); __pthread_once_fork_parent(); + pthread_call_handlers(pthread_atfork_parent); + + pthread_mutex_unlock(&pthread_atfork_lock); } + return pid; } diff --git a/posix/Makefile b/posix/Makefile index 468a078e06..9837f33d2e 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -66,7 +66,7 @@ aux := init-posix environ tests := tstgetopt testfnm runtests runptests \ tst-preadwrite tst-preadwrite64 test-vfork regexbug1 \ tst-getlogin tst-mmap tst-getaddrinfo tst-truncate \ - tst-truncate64 + tst-truncate64 tst-fork ifeq (yes,$(build-shared)) test-srcs := globtest tests += wordexp-test diff --git a/posix/tst-fork.c b/posix/tst-fork.c new file mode 100644 index 0000000000..287fa5ffb5 --- /dev/null +++ b/posix/tst-fork.c @@ -0,0 +1,136 @@ +/* Tests for fork. + Copyright (C) 2000 Free Software Foundation, Inc. + Contributed by Ulrich Drepper , 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include + + +static const char testdata[] = "This is a test"; +static const char testdata2[] = "And here we go again"; + + +int +main (void) +{ + const char *tmpdir = getenv ("TMPDIR"); + char buf[100]; + size_t tmpdirlen; + char *name; + int fd; + pid_t pid; + off_t off; + int status; + + if (tmpdir == NULL || *tmpdir == '\0') + tmpdir = "/tmp"; + tmpdirlen = strlen (tmpdir); + + name = (char *) malloc (tmpdirlen + strlen ("/forkXXXXXX") + 1); + if (name == NULL) + error (EXIT_FAILURE, errno, "cannot allocate file name"); + + mempcpy (mempcpy (name, tmpdir, tmpdirlen), + "/forkXXXXXX", sizeof ("/forkXXXXXX")); + + /* Open our test file. */ + fd = mkstemp (name); + if (fd == -1) + error (EXIT_FAILURE, errno, "cannot open test file `%s'", name); + + /* Make sure it gets removed. */ + unlink (name); + + /* Write some data. */ + if (write (fd, testdata, strlen (testdata)) != strlen (testdata)) + error (EXIT_FAILURE, errno, "cannot write test data"); + + /* Get the position in the stream. */ + off = lseek (fd, 0, SEEK_CUR); + if (off == (off_t) -1 || off != strlen (testdata)) + error (EXIT_FAILURE, errno, "wrong file position"); + + /* Now fork of the process. */ + pid = fork (); + if (pid == 0) + { + /* This is the child. First get the position of the descriptor. */ + off = lseek (fd, 0, SEEK_CUR); + if (off == (off_t) -1 || off != strlen (testdata)) + error (EXIT_FAILURE, errno, "wrong file position in child"); + + /* Reset the position. */ + if (lseek (fd, 0, SEEK_SET) != 0) + error (EXIT_FAILURE, errno, "cannot reset position in child"); + + /* Read the data. */ + if (read (fd, buf, sizeof buf) != strlen (testdata)) + error (EXIT_FAILURE, errno, "cannot read data in child"); + + /* Compare the data. */ + if (memcmp (buf, testdata, strlen (testdata)) != 0) + error (EXIT_FAILURE, 0, "data comparison failed in child"); + + /* Reset position again. */ + if (lseek (fd, 0, SEEK_SET) != 0) + error (EXIT_FAILURE, errno, "cannot reset position again in child"); + + /* Write new data. */ + if (write (fd, testdata2, strlen (testdata2)) != strlen (testdata2)) + error (EXIT_FAILURE, errno, "cannot write new data in child"); + + /* Close the file. This must not remove it. */ + close (fd); + + _exit (0); + } + else if (pid < 0) + /* Something went wrong. */ + error (EXIT_FAILURE, errno, "cannot fork"); + + /* Wait for the child. */ + if (waitpid (pid, &status, 0) != pid) + error (EXIT_FAILURE, 0, "Oops, wrong test program terminated"); + + if (WTERMSIG (status) != 0) + error (EXIT_FAILURE, 0, "Child terminated incorrectly"); + status = WEXITSTATUS (status); + + if (status == 0) + { + /* Test whether the child wrote the right data. First test the + position. It must be the same as in the child. */ + if (lseek (fd, 0, SEEK_CUR) != strlen (testdata2)) + error (EXIT_FAILURE, 0, "file position not changed"); + + if (lseek (fd, 0, SEEK_SET) != 0) + error (EXIT_FAILURE, errno, "cannot reset file position"); + + if (read (fd, buf, sizeof buf) != strlen (testdata2)) + error (EXIT_FAILURE, errno, "cannot read new data"); + + if (memcmp (buf, testdata2, strlen (testdata2)) != 0) + error (EXIT_FAILURE, 0, "new data not read correctly"); + } + + return status; +} -- cgit 1.4.1