* posix/Makefile (tests): Add tst-fork.
This commit is contained in:
Ulrich Drepper 2000-05-28 22:14:55 +00:00
parent 2588068bdf
commit c0f3519d2e
8 changed files with 288 additions and 10 deletions

View File

@ -1,5 +1,7 @@
2000-05-28 Ulrich Drepper <drepper@redhat.com>
* posix/Makefile (tests): Add tst-fork.
* resolv/inet_pton.c: Loose __P.
2000-05-21 Jakub Jelinek <jakub@redhat.com>

View File

@ -1,3 +1,23 @@
2000-05-28 Ulrich Drepper <drepper@redhat.com>
* Makefile (tests): Add ex8.
* Examples/ex8.c: New file.
2000-05-12 Kaz Kylheku <kaz@ashi.footprints.net>
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 <drepper@redhat.com>
* Makefile (libpthread-routines): Add pspinlock.

101
linuxthreads/Examples/ex8.c Normal file
View File

@ -0,0 +1,101 @@
/* Tests for fork in multi-threaded environment.
Copyright (C) 2000 Free Software Foundation, Inc.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 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 <errno.h>
#include <error.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <wait.h>
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;
}

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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

136
posix/tst-fork.c Normal file
View File

@ -0,0 +1,136 @@
/* Tests for fork.
Copyright (C) 2000 Free Software Foundation, Inc.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 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 <errno.h>
#include <error.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
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;
}