From ca78735936e87a2b72c553636e815bd555c4ef7e Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 28 Dec 2002 08:45:26 +0000 Subject: [PATCH] Test of cancellation handling. --- linuxthreads/tst-cancel1.c | 150 ++++++++++++ linuxthreads/tst-cancel2.c | 100 ++++++++ linuxthreads/tst-cancel3.c | 98 ++++++++ linuxthreads/tst-cancel4.c | 466 +++++++++++++++++++++++++++++++++++++ linuxthreads/tst-cancel5.c | 1 + linuxthreads/tst-cancel6.c | 79 +++++++ 6 files changed, 894 insertions(+) create mode 100644 linuxthreads/tst-cancel1.c create mode 100644 linuxthreads/tst-cancel2.c create mode 100644 linuxthreads/tst-cancel3.c create mode 100644 linuxthreads/tst-cancel4.c create mode 100644 linuxthreads/tst-cancel5.c create mode 100644 linuxthreads/tst-cancel6.c diff --git a/linuxthreads/tst-cancel1.c b/linuxthreads/tst-cancel1.c new file mode 100644 index 0000000000..99a8339f0c --- /dev/null +++ b/linuxthreads/tst-cancel1.c @@ -0,0 +1,150 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 2002. + + 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; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + + +static pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; + +static int cntr; + + +static void +cleanup (void *arg) +{ + if (arg != (void *) 42l) + cntr = 42; + else + cntr = 1; +} + + +static void * +tf (void *arg) +{ + int err; + + pthread_cleanup_push (cleanup, (void *) 42l); + + err = pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + if (err != 0) + { + printf ("setcanceltype failed: %s\n", strerror (err)); + exit (1); + } + + err = pthread_mutex_unlock (&m2); + if (err != 0) + { + printf ("child: mutex_unlock failed: %s\n", strerror (err)); + exit (1); + } + + err = pthread_mutex_lock (&m1); + if (err != 0) + { + printf ("child: 1st mutex_lock failed: %s\n", strerror (err)); + exit (1); + } + + /* We should never come here. */ + + pthread_cleanup_pop (0); + + return NULL; +} + + +static int +do_test (void) +{ + int err; + pthread_t th; + int result = 0; + void *retval; + + /* Get the mutexes. */ + err = pthread_mutex_lock (&m1); + if (err != 0) + { + printf ("parent: 1st mutex_lock failed: %s\n", strerror (err)); + return 1; + } + err = pthread_mutex_lock (&m2); + if (err != 0) + { + printf ("parent: 2nd mutex_lock failed: %s\n", strerror (err)); + return 1; + } + + err = pthread_create (&th, NULL, tf, NULL); + if (err != 0) + { + printf ("create failed: %s\n", strerror (err)); + return 1; + } + + err = pthread_mutex_lock (&m2); + if (err != 0) + { + printf ("parent: 3rd mutex_lock failed: %s\n", strerror (err)); + return 1; + } + + err = pthread_cancel (th); + if (err != 0) + { + printf ("cancel failed: %s\n", strerror (err)); + return 1; + } + + err = pthread_join (th, &retval); + if (err != 0) + { + printf ("join failed: %s\n", strerror (err)); + return 1; + } + + if (retval != PTHREAD_CANCELED) + { + printf ("wrong return value: %p\n", retval); + result = 1; + } + + if (cntr == 42) + { + puts ("cleanup handler called with wrong argument"); + result = 1; + } + else if (cntr != 1) + { + puts ("cleanup handling not called"); + result = 1; + } + + return result; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/linuxthreads/tst-cancel2.c b/linuxthreads/tst-cancel2.c new file mode 100644 index 0000000000..6d80f8ae5e --- /dev/null +++ b/linuxthreads/tst-cancel2.c @@ -0,0 +1,100 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 2002. + + 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; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + + +static int fd[2]; + + +static void * +tf (void *arg) +{ + /* The buffer size must be larger than the pipe size so that the + write blocks. */ + char buf[100000]; + + if (write (fd[1], buf, sizeof (buf)) == sizeof (buf)) + { + puts ("write succeeded"); + return (void *) 1l; + } + + return (void *) 42l; +} + + +static int +do_test (void) +{ + pthread_t th; + void *r; + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + + if (sigaction (SIGPIPE, &sa, NULL) != 0) + { + puts ("sigaction failed"); + return 1; + } + + if (pipe (fd) != 0) + { + puts ("pipe failed"); + return 1; + } + + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("create failed"); + return 1; + } + + if (pthread_cancel (th) != 0) + { + puts ("cancel failed"); + return 1; + } + + /* This will cause the write in the child to return. */ + close (fd[0]); + + if (pthread_join (th, &r) != 0) + { + puts ("join failed"); + return 1; + } + + if (r != PTHREAD_CANCELED) + { + printf ("result is wrong: expected %p, got %p\n", PTHREAD_CANCELED, r); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/linuxthreads/tst-cancel3.c b/linuxthreads/tst-cancel3.c new file mode 100644 index 0000000000..86c482bcc1 --- /dev/null +++ b/linuxthreads/tst-cancel3.c @@ -0,0 +1,98 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 2002. + + 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; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + + +static int fd[2]; + + +static void * +tf (void *arg) +{ + char buf[100]; + + if (read (fd[0], buf, sizeof (buf)) == sizeof (buf)) + { + puts ("read succeeded"); + return (void *) 1l; + } + + return NULL; +} + + +static int +do_test (void) +{ + pthread_t th; + void *r; + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + + if (sigaction (SIGPIPE, &sa, NULL) != 0) + { + puts ("sigaction failed"); + return 1; + } + + if (pipe (fd) != 0) + { + puts ("pipe failed"); + return 1; + } + + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("create failed"); + return 1; + } + + if (pthread_cancel (th) != 0) + { + puts ("cancel failed"); + return 1; + } + + /* This will cause the read in the child to return. */ + close (fd[0]); + + if (pthread_join (th, &r) != 0) + { + puts ("join failed"); + return 1; + } + + if (r != PTHREAD_CANCELED) + { + puts ("result is wrong"); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/linuxthreads/tst-cancel4.c b/linuxthreads/tst-cancel4.c new file mode 100644 index 0000000000..9394848b5b --- /dev/null +++ b/linuxthreads/tst-cancel4.c @@ -0,0 +1,466 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 2002. + + 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; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* NOTE: this tests functionality beyond POSIX. POSIX does not allow + exit to be called more than once. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The following interfaces are defined to be cancellation points but + tests are not yet implemented: + + accept() aio_suspend() clock_nanosleep() + close() connect() creat() + fcntl() fsync() getmsg() + getpmsg() lockf() mq_receive() + mq_send() mq_timedreceive() mq_timedsend() + msgrcv() msgsnd() msync() + open() pause() + pread() pthread_cond_timedwait() + pthread_cond_wait() pthread_join() pthread_testcancel() + putmsg() putpmsg() pwrite() + recv() + recvfrom() recvmsg() + sem_timedwait() sem_wait() send() + sendmsg() sendto() sigpause() + sigsuspend() sigtimedwait() sigwait() + sigwaitinfo() system() + tcdrain() + + Since STREAMS are not supported in the standard Linux kernel there + is no need to test the STREAMS related functions. +*/ + +/* Pipe descriptors. */ +static int fds[2]; + +/* Often used barrier for two threads. */ +static pthread_barrier_t b2; + + +static void * +tf_read (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + char buf[100]; + ssize_t s = read (fds[0], buf, sizeof (buf)); + + printf ("%s: read returns with %zd\n", __FUNCTION__, s); + + exit (1); +} + + +static void * +tf_readv (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + char buf[100]; + struct iovec iov[1] = { [0] = { .iov_base = buf, .iov_len = sizeof (buf) } }; + ssize_t s = readv (fds[0], iov, 1); + + printf ("%s: readv returns with %zd\n", __FUNCTION__, s); + + exit (1); +} + + +static void * +tf_write (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + char buf[100000]; + memset (buf, '\0', sizeof (buf)); + ssize_t s = write (fds[1], buf, sizeof (buf)); + + printf ("%s: write returns with %zd\n", __FUNCTION__, s); + + exit (1); +} + + +static void * +tf_writev (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + char buf[100000]; + memset (buf, '\0', sizeof (buf)); + struct iovec iov[1] = { [0] = { .iov_base = buf, .iov_len = sizeof (buf) } }; + ssize_t s = writev (fds[1], iov, 1); + + printf ("%s: writev returns with %zd\n", __FUNCTION__, s); + + exit (1); +} + + +static void * +tf_sleep (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + sleep (10000000); + + printf ("%s: sleep returns\n", __FUNCTION__); + + exit (1); +} + + +static void * +tf_usleep (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + usleep ((useconds_t) ULONG_MAX); + + printf ("%s: usleep returns\n", __FUNCTION__); + + exit (1); +} + + +static void * +tf_nanosleep (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + struct timespec ts = { .tv_sec = 10000000, .tv_nsec = 0 }; + while (nanosleep (&ts, &ts) != 0) + continue; + + printf ("%s: nanosleep returns\n", __FUNCTION__); + + exit (1); +} + + +static void * +tf_select (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + fd_set rfs; + FD_ZERO (&rfs); + FD_SET (fds[0], &rfs); + + int s = select (fds[0] + 1, &rfs, NULL, NULL, NULL); + + printf ("%s: select returns with %d (%s)\n", __FUNCTION__, s, + strerror (errno)); + + exit (1); +} + + +static void * +tf_pselect (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + fd_set rfs; + FD_ZERO (&rfs); + FD_SET (fds[0], &rfs); + + int s = pselect (fds[0] + 1, &rfs, NULL, NULL, NULL, NULL); + + printf ("%s: pselect returns with %d (%s)\n", __FUNCTION__, s, + strerror (errno)); + + exit (1); +} + + +static void * +tf_poll (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + struct pollfd rfs[1] = { [0] = { .fd = fds[0], .events = POLLIN } }; + + int s = poll (rfs, 1, -1); + + printf ("%s: poll returns with %d (%s)\n", __FUNCTION__, s, + strerror (errno)); + + exit (1); +} + + +static void * +tf_wait (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + pid_t pid = fork (); + if (pid == -1) + { + puts ("fork failed"); + exit (1); + } + + if (pid == 0) + { + /* Make the program disappear after a while. */ + sleep (10); + exit (0); + } + + int s = wait (NULL); + + printf ("%s: wait returns with %d (%s)\n", __FUNCTION__, s, + strerror (errno)); + + exit (1); +} + + +static void * +tf_waitpid (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + pid_t pid = fork (); + if (pid == -1) + { + puts ("fork failed"); + exit (1); + } + + if (pid == 0) + { + /* Make the program disappear after a while. */ + sleep (10); + exit (0); + } + + int s = waitpid (-1, NULL, 0); + + printf ("%s: waitpid returns with %d (%s)\n", __FUNCTION__, s, + strerror (errno)); + + exit (1); +} + + +static void * +tf_waitid (void *arg) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + pid_t pid = fork (); + if (pid == -1) + { + puts ("fork failed"); + exit (1); + } + + if (pid == 0) + { + /* Make the program disappear after a while. */ + sleep (10); + exit (0); + } + + siginfo_t si; + int s = waitid (P_PID, pid, &si, 0); + + printf ("%s: waitid returns with %d (%s)\n", __FUNCTION__, s, + strerror (errno)); + + exit (1); +} + + +static struct +{ + void *(*tf) (void *); + int nb; +} tests[] = +{ + { tf_read, 2 }, + { tf_readv, 2 }, + { tf_select, 2 }, + { tf_pselect, 2 }, + { tf_poll, 2 }, + { tf_write, 2 }, + { tf_writev, 2}, + { tf_sleep, 2 }, + { tf_usleep, 2 }, + { tf_nanosleep, 2 }, + { tf_wait, 2 }, + { tf_waitid, 2 }, + { tf_waitpid, 2 }, +}; +#define ntest_tf (sizeof (tests) / sizeof (tests[0])) + + +static int +do_test (void) +{ + if (pipe (fds) != 0) + { + puts ("pipe failed"); + exit (1); + } + + int cnt; + for (cnt = 0; cnt < ntest_tf; ++cnt) + { + printf ("round %d\n", cnt); + + if (pthread_barrier_init (&b2, NULL, tests[cnt].nb) != 0) + { + puts ("b2 init failed"); + exit (1); + } + + pthread_t th; + if (pthread_create (&th, NULL, tests[cnt].tf, NULL) != 0) + { + printf ("create for round %d test failed\n", cnt); + exit (1); + } + + puts ("barrier waits"); + + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + puts ("nanosleep delay"); + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; + while (nanosleep (&ts, &ts) != 0) + continue; + + if (pthread_cancel (th) != 0) + { + printf ("cancel in round %d failed\n", cnt); + exit (1); + } + + void *status; + if (pthread_join (th, &status) != 0) + { + printf ("join in round %d failed\n", cnt); + exit (1); + } + if (status != PTHREAD_CANCELED) + { + printf ("thread in round %d not canceled\n", cnt); + exit (1); + } + printf ("test %d successful\n", cnt); + + if (pthread_barrier_destroy (&b2) != 0) + { + puts ("barrier_destroy failed"); + exit (1); + } + } + + return 0; +} + +#define TIMEOUT 60 +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/linuxthreads/tst-cancel5.c b/linuxthreads/tst-cancel5.c new file mode 100644 index 0000000000..1c879eba8b --- /dev/null +++ b/linuxthreads/tst-cancel5.c @@ -0,0 +1 @@ +#include "tst-cancel4.c" diff --git a/linuxthreads/tst-cancel6.c b/linuxthreads/tst-cancel6.c new file mode 100644 index 0000000000..94de85830b --- /dev/null +++ b/linuxthreads/tst-cancel6.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 2002. + + 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; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + + +static void * +tf (void *arg) +{ + char buf[100]; + fgets (buf, sizeof (buf), arg); + /* This call should never return. */ + return NULL; +} + + +static int +do_test (void) +{ + int fd[2]; + if (pipe (fd) != 0) + { + puts ("pipe failed"); + return 1; + } + + FILE *fp = fdopen (fd[0], "r"); + if (fp == NULL) + { + puts ("fdopen failed"); + return 1; + } + + pthread_t th; + if (pthread_create (&th, NULL, tf, fp) != 0) + { + puts ("pthread_create failed"); + return 1; + } + + sleep (1); + + if (pthread_cancel (th) != 0) + { + puts ("pthread_cancel failed"); + return 1; + } + + void *r; + if (pthread_join (th, &r) != 0) + { + puts ("pthread_join failed"); + return 1; + } + + return r != PTHREAD_CANCELED; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c"