diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | nptl/Makefile | 3 | ||||
-rw-r--r-- | nptl/tst-cancel4-common.c | 257 | ||||
-rw-r--r-- | nptl/tst-cancel4-common.h | 77 | ||||
-rw-r--r-- | nptl/tst-cancel4.c | 323 | ||||
-rw-r--r-- | nptl/tst-cancel4_1.c | 127 | ||||
-rw-r--r-- | nptl/tst-cancel4_2.c | 125 |
7 files changed, 613 insertions, 309 deletions
@@ -1,3 +1,13 @@ +2016-06-13 Adhemerval Zanella <adhemerval.zanella@linaro.org> + + * nptl/Makefile (test): Add tst-cancel4_1 and tst-cancel4_2. + * nptl/tst-cancel4-common.c: New file. + * nptl/tst-cancel4-common.h: Likewise. + * nptl/tst-cancel4.c: Move common definitions to + tst-cancel4-common.{c,h} file. + * nptl/tst-cancel4_1.c: New test. + * nptl/tst-cancel4_2.c: New test. + 2016-06-13 Florian Weimer <fweimer@redhat.com> [BZ #20248] diff --git a/nptl/Makefile b/nptl/Makefile index a159b58..e0bc1b7 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -255,7 +255,8 @@ tests = tst-typesizes \ tst-tls1 tst-tls2 \ tst-fork1 tst-fork2 tst-fork3 tst-fork4 \ tst-atfork1 \ - tst-cancel1 tst-cancel2 tst-cancel3 tst-cancel4 tst-cancel5 \ + tst-cancel1 tst-cancel2 tst-cancel3 tst-cancel4 tst-cancel4_1 \ + tst-cancel4_2 tst-cancel5 \ tst-cancel6 tst-cancel7 tst-cancel8 tst-cancel9 tst-cancel10 \ tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \ tst-cancel16 tst-cancel17 tst-cancel18 tst-cancel19 tst-cancel20 \ diff --git a/nptl/tst-cancel4-common.c b/nptl/tst-cancel4-common.c new file mode 100644 index 0000000..f235d53 --- /dev/null +++ b/nptl/tst-cancel4-common.c @@ -0,0 +1,257 @@ +/* Common file for all tst-cancel4_* + + 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; if not, see + <http://www.gnu.org/licenses/>. */ + +static int +do_test (void) +{ + int val; + socklen_t len; + + if (socketpair (AF_UNIX, SOCK_STREAM, PF_UNIX, fds) != 0) + { + perror ("socketpair"); + exit (1); + } + + val = 1; + len = sizeof(val); + setsockopt (fds[1], SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)); + if (getsockopt (fds[1], SOL_SOCKET, SO_SNDBUF, &val, &len) < 0) + { + perror ("getsockopt"); + exit (1); + } + if (val >= WRITE_BUFFER_SIZE) + { + puts ("minimum write buffer size too large"); + exit (1); + } + setsockopt (fds[1], SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)); + + int result = 0; + size_t cnt; + for (cnt = 0; cnt < ntest_tf; ++cnt) + { + if (tests[cnt].only_early) + continue; + + if (pthread_barrier_init (&b2, NULL, tests[cnt].nb) != 0) + { + puts ("b2 init failed"); + exit (1); + } + + /* Reset the counter for the cleanup handler. */ + cl_called = 0; + + pthread_t th; + if (pthread_create (&th, NULL, tests[cnt].tf, NULL) != 0) + { + printf ("create for '%s' test failed\n", tests[cnt].name); + result = 1; + continue; + } + + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + result = 1; + continue; + } + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; + while (nanosleep (&ts, &ts) != 0) + continue; + + if (pthread_cancel (th) != 0) + { + printf ("cancel for '%s' failed\n", tests[cnt].name); + result = 1; + continue; + } + + void *status; + if (pthread_join (th, &status) != 0) + { + printf ("join for '%s' failed\n", tests[cnt].name); + result = 1; + continue; + } + if (status != PTHREAD_CANCELED) + { + printf ("thread for '%s' not canceled\n", tests[cnt].name); + result = 1; + continue; + } + + if (pthread_barrier_destroy (&b2) != 0) + { + puts ("barrier_destroy failed"); + result = 1; + continue; + } + + if (cl_called == 0) + { + printf ("cleanup handler not called for '%s'\n", tests[cnt].name); + result = 1; + continue; + } + if (cl_called > 1) + { + printf ("cleanup handler called more than once for '%s'\n", + tests[cnt].name); + result = 1; + continue; + } + + printf ("in-time cancel test of '%s' successful\n", tests[cnt].name); + + if (tempfd != -1) + { + close (tempfd); + tempfd = -1; + } + if (tempfd2 != -1) + { + close (tempfd2); + tempfd2 = -1; + } + if (tempfname != NULL) + { + unlink (tempfname); + free (tempfname); + tempfname = NULL; + } + if (tempmsg != -1) + { + msgctl (tempmsg, IPC_RMID, NULL); + tempmsg = -1; + } + } + + for (cnt = 0; cnt < ntest_tf; ++cnt) + { + if (pthread_barrier_init (&b2, NULL, tests[cnt].nb) != 0) + { + puts ("b2 init failed"); + exit (1); + } + + /* Reset the counter for the cleanup handler. */ + cl_called = 0; + + pthread_t th; + if (pthread_create (&th, NULL, tests[cnt].tf, (void *) 1l) != 0) + { + printf ("create for '%s' test failed\n", tests[cnt].name); + result = 1; + continue; + } + + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + result = 1; + continue; + } + + if (pthread_cancel (th) != 0) + { + printf ("cancel for '%s' failed\n", tests[cnt].name); + result = 1; + continue; + } + + r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + result = 1; + continue; + } + + void *status; + if (pthread_join (th, &status) != 0) + { + printf ("join for '%s' failed\n", tests[cnt].name); + result = 1; + continue; + } + if (status != PTHREAD_CANCELED) + { + printf ("thread for '%s' not canceled\n", tests[cnt].name); + result = 1; + continue; + } + + if (pthread_barrier_destroy (&b2) != 0) + { + puts ("barrier_destroy failed"); + result = 1; + continue; + } + + if (cl_called == 0) + { + printf ("cleanup handler not called for '%s'\n", tests[cnt].name); + result = 1; + continue; + } + if (cl_called > 1) + { + printf ("cleanup handler called more than once for '%s'\n", + tests[cnt].name); + result = 1; + continue; + } + + printf ("early cancel test of '%s' successful\n", tests[cnt].name); + + if (tempfd != -1) + { + close (tempfd); + tempfd = -1; + } + if (tempfd2 != -1) + { + close (tempfd2); + tempfd2 = -1; + } + if (tempfname != NULL) + { + unlink (tempfname); + free (tempfname); + tempfname = NULL; + } + if (tempmsg != -1) + { + msgctl (tempmsg, IPC_RMID, NULL); + tempmsg = -1; + } + } + + return result; +} + +#define TIMEOUT 60 +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-cancel4-common.h b/nptl/tst-cancel4-common.h new file mode 100644 index 0000000..e1683c4 --- /dev/null +++ b/nptl/tst-cancel4-common.h @@ -0,0 +1,77 @@ +/* Common definition for tst-cancel4_* tests. + + 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; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> + +/* Pipe descriptors. */ +static int fds[2]; + +/* Temporary file descriptor, to be closed after each round. */ +static int tempfd = -1; +static int tempfd2 = -1; +/* Name of temporary file to be removed after each round. */ +static char *tempfname; +/* Temporary message queue. */ +static int tempmsg = -1; + +/* Often used barrier for two threads. */ +static pthread_barrier_t b2; + +/* The WRITE_BUFFER_SIZE value needs to be chosen such that if we set + the socket send buffer size to '1', a write of this size on that + socket will block. + + The Linux kernel imposes a minimum send socket buffer size which + has changed over the years. As of Linux 3.10 the value is: + + 2 * (2048 + SKB_DATA_ALIGN(sizeof(struct sk_buff))) + + which is attempting to make sure that with standard MTUs, + TCP can always queue up at least 2 full sized packets. + + Furthermore, there is logic in the socket send paths that + will allow one more packet (of any size) to be queued up as + long as some socket buffer space remains. Blocking only + occurs when we try to queue up a new packet and the send + buffer space has already been fully consumed. + + Therefore we must set this value to the largest possible value of + the formula above (and since it depends upon the size of "struct + sk_buff", it is dependent upon machine word size etc.) plus some + slack space. */ + +#define WRITE_BUFFER_SIZE 16384 + +/* Cleanup handling test. */ +static int cl_called; + +static void +cl (void *arg) +{ + ++cl_called; +} + +struct cancel_tests +{ + const char *name; + void *(*tf) (void *); + int nb; + int only_early; +}; +#define ADD_TEST(name, nbar, early) { #name, tf_##name, nbar, early } diff --git a/nptl/tst-cancel4.c b/nptl/tst-cancel4.c index 60f7ead..4221af4 100644 --- a/nptl/tst-cancel4.c +++ b/nptl/tst-cancel4.c @@ -19,23 +19,21 @@ /* NOTE: this tests functionality beyond POSIX. POSIX does not allow exit to be called more than once. */ -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <pthread.h> -#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <termios.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/ipc.h> +#include <sys/msg.h> #include <unistd.h> +#include <errno.h> +#include <pthread.h> +#include <fcntl.h> +#include <termios.h> #include <sys/mman.h> -#include <sys/msg.h> #include <sys/poll.h> -#include <sys/select.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <sys/un.h> #include <sys/wait.h> #include "pthreadP.h" @@ -62,62 +60,17 @@ aio_suspend() is tested in tst-cancel17. clock_nanosleep() is tested in tst-cancel18. -*/ - -/* Pipe descriptors. */ -static int fds[2]; -/* Temporary file descriptor, to be closed after each round. */ -static int tempfd = -1; -static int tempfd2 = -1; -/* Name of temporary file to be removed after each round. */ -static char *tempfname; -/* Temporary message queue. */ -static int tempmsg = -1; - -/* Often used barrier for two threads. */ -static pthread_barrier_t b2; + Linux sendmmsg and recvmmsg are checked in tst-cancel4_1.c and + tst-cancel4_2.c respectively. +*/ +#include "tst-cancel4-common.h" #ifndef IPC_ADDVAL # define IPC_ADDVAL 0 #endif -/* The WRITE_BUFFER_SIZE value needs to be chosen such that if we set - the socket send buffer size to '1', a write of this size on that - socket will block. - - The Linux kernel imposes a minimum send socket buffer size which - has changed over the years. As of Linux 3.10 the value is: - - 2 * (2048 + SKB_DATA_ALIGN(sizeof(struct sk_buff))) - - which is attempting to make sure that with standard MTUs, - TCP can always queue up at least 2 full sized packets. - - Furthermore, there is logic in the socket send paths that - will allow one more packet (of any size) to be queued up as - long as some socket buffer space remains. Blocking only - occurs when we try to queue up a new packet and the send - buffer space has already been fully consumed. - - Therefore we must set this value to the largest possible value of - the formula above (and since it depends upon the size of "struct - sk_buff", it is dependent upon machine word size etc.) plus some - slack space. */ - -#define WRITE_BUFFER_SIZE 16384 - -/* Cleanup handling test. */ -static int cl_called; - -static void -cl (void *arg) -{ - ++cl_called; -} - - static void * tf_read (void *arg) @@ -1391,7 +1344,6 @@ tf_recvmsg (void *arg) exit (1); } - static void * tf_open (void *arg) { @@ -2196,15 +2148,8 @@ tf_msgsnd (void *arg) } -static struct -{ - const char *name; - void *(*tf) (void *); - int nb; - int only_early; -} tests[] = +struct cancel_tests tests[] = { -#define ADD_TEST(name, nbar, early) { #name, tf_##name, nbar, early } ADD_TEST (read, 2, 0), ADD_TEST (readv, 2, 0), ADD_TEST (select, 2, 0), @@ -2249,242 +2194,4 @@ static struct }; #define ntest_tf (sizeof (tests) / sizeof (tests[0])) - -static int -do_test (void) -{ - int val; - socklen_t len; - - if (socketpair (AF_UNIX, SOCK_STREAM, PF_UNIX, fds) != 0) - { - perror ("socketpair"); - exit (1); - } - - val = 1; - len = sizeof(val); - setsockopt (fds[1], SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)); - if (getsockopt (fds[1], SOL_SOCKET, SO_SNDBUF, &val, &len) < 0) - { - perror ("getsockopt"); - exit (1); - } - if (val >= WRITE_BUFFER_SIZE) - { - puts ("minimum write buffer size too large"); - exit (1); - } - setsockopt (fds[1], SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)); - - int result = 0; - size_t cnt; - for (cnt = 0; cnt < ntest_tf; ++cnt) - { - if (tests[cnt].only_early) - continue; - - if (pthread_barrier_init (&b2, NULL, tests[cnt].nb) != 0) - { - puts ("b2 init failed"); - exit (1); - } - - /* Reset the counter for the cleanup handler. */ - cl_called = 0; - - pthread_t th; - if (pthread_create (&th, NULL, tests[cnt].tf, NULL) != 0) - { - printf ("create for '%s' test failed\n", tests[cnt].name); - result = 1; - continue; - } - - int r = pthread_barrier_wait (&b2); - if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) - { - printf ("%s: barrier_wait failed\n", __FUNCTION__); - result = 1; - continue; - } - - struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 }; - while (nanosleep (&ts, &ts) != 0) - continue; - - if (pthread_cancel (th) != 0) - { - printf ("cancel for '%s' failed\n", tests[cnt].name); - result = 1; - continue; - } - - void *status; - if (pthread_join (th, &status) != 0) - { - printf ("join for '%s' failed\n", tests[cnt].name); - result = 1; - continue; - } - if (status != PTHREAD_CANCELED) - { - printf ("thread for '%s' not canceled\n", tests[cnt].name); - result = 1; - continue; - } - - if (pthread_barrier_destroy (&b2) != 0) - { - puts ("barrier_destroy failed"); - result = 1; - continue; - } - - if (cl_called == 0) - { - printf ("cleanup handler not called for '%s'\n", tests[cnt].name); - result = 1; - continue; - } - if (cl_called > 1) - { - printf ("cleanup handler called more than once for '%s'\n", - tests[cnt].name); - result = 1; - continue; - } - - printf ("in-time cancel test of '%s' successful\n", tests[cnt].name); - - if (tempfd != -1) - { - close (tempfd); - tempfd = -1; - } - if (tempfd2 != -1) - { - close (tempfd2); - tempfd2 = -1; - } - if (tempfname != NULL) - { - unlink (tempfname); - free (tempfname); - tempfname = NULL; - } - if (tempmsg != -1) - { - msgctl (tempmsg, IPC_RMID, NULL); - tempmsg = -1; - } - } - - for (cnt = 0; cnt < ntest_tf; ++cnt) - { - if (pthread_barrier_init (&b2, NULL, tests[cnt].nb) != 0) - { - puts ("b2 init failed"); - exit (1); - } - - /* Reset the counter for the cleanup handler. */ - cl_called = 0; - - pthread_t th; - if (pthread_create (&th, NULL, tests[cnt].tf, (void *) 1l) != 0) - { - printf ("create for '%s' test failed\n", tests[cnt].name); - result = 1; - continue; - } - - int r = pthread_barrier_wait (&b2); - if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) - { - printf ("%s: barrier_wait failed\n", __FUNCTION__); - result = 1; - continue; - } - - if (pthread_cancel (th) != 0) - { - printf ("cancel for '%s' failed\n", tests[cnt].name); - result = 1; - continue; - } - - r = pthread_barrier_wait (&b2); - if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) - { - printf ("%s: barrier_wait failed\n", __FUNCTION__); - result = 1; - continue; - } - - void *status; - if (pthread_join (th, &status) != 0) - { - printf ("join for '%s' failed\n", tests[cnt].name); - result = 1; - continue; - } - if (status != PTHREAD_CANCELED) - { - printf ("thread for '%s' not canceled\n", tests[cnt].name); - result = 1; - continue; - } - - if (pthread_barrier_destroy (&b2) != 0) - { - puts ("barrier_destroy failed"); - result = 1; - continue; - } - - if (cl_called == 0) - { - printf ("cleanup handler not called for '%s'\n", tests[cnt].name); - result = 1; - continue; - } - if (cl_called > 1) - { - printf ("cleanup handler called more than once for '%s'\n", - tests[cnt].name); - result = 1; - continue; - } - - printf ("early cancel test of '%s' successful\n", tests[cnt].name); - - if (tempfd != -1) - { - close (tempfd); - tempfd = -1; - } - if (tempfd2 != -1) - { - close (tempfd2); - tempfd2 = -1; - } - if (tempfname != NULL) - { - unlink (tempfname); - free (tempfname); - tempfname = NULL; - } - if (tempmsg != -1) - { - msgctl (tempmsg, IPC_RMID, NULL); - tempmsg = -1; - } - } - - return result; -} - -#define TIMEOUT 60 -#define TEST_FUNCTION do_test () -#include "../test-skeleton.c" +#include "tst-cancel4-common.c" diff --git a/nptl/tst-cancel4_1.c b/nptl/tst-cancel4_1.c new file mode 100644 index 0000000..68296ec --- /dev/null +++ b/nptl/tst-cancel4_1.c @@ -0,0 +1,127 @@ +/* Check sendmmsg cancellation. + + 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; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/ipc.h> +#include <sys/msg.h> +#include <unistd.h> +#include <errno.h> +#include <pthread.h> + +#include "tst-cancel4-common.h" + +static void * +tf_sendmmsg (void *arg) +{ + if (arg == NULL) + // XXX If somebody can provide a portable test case in which sendmmsg() + // blocks we can enable this test to run in both rounds. + abort (); + + struct sockaddr_un sun; + + tempfd = socket (AF_UNIX, SOCK_DGRAM, 0); + if (tempfd == -1) + { + printf ("%s: first socket call failed\n", __FUNCTION__); + exit (1); + } + + int tries = 0; + do + { + if (++tries > 10) + { + printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__); + } + + strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-7-XXXXXX"); + if (mktemp (sun.sun_path) == NULL) + { + printf ("%s: cannot generate temp file name\n", __FUNCTION__); + exit (1); + } + + sun.sun_family = AF_UNIX; + } + while (bind (tempfd, (struct sockaddr *) &sun, + offsetof (struct sockaddr_un, sun_path) + + strlen (sun.sun_path) + 1) != 0); + tempfname = strdup (sun.sun_path); + + tempfd2 = socket (AF_UNIX, SOCK_DGRAM, 0); + if (tempfd2 == -1) + { + printf ("%s: second socket call failed\n", __FUNCTION__); + exit (1); + } + + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + pthread_cleanup_push (cl, NULL); + + char mem[1]; + struct iovec iov[1]; + iov[0].iov_base = mem; + iov[0].iov_len = 1; + + struct mmsghdr mm; + mm.msg_hdr.msg_name = &sun; + mm.msg_hdr.msg_namelen = (offsetof (struct sockaddr_un, sun_path) + + strlen (sun.sun_path) + 1); + mm.msg_hdr.msg_iov = iov; + mm.msg_hdr.msg_iovlen = 1; + mm.msg_hdr.msg_control = NULL; + mm.msg_hdr.msg_controllen = 0; + + ssize_t ret = sendmmsg (tempfd2, &mm, 1, 0); + if (ret == -1 && errno == ENOSYS) + exit (77); + + pthread_cleanup_pop (0); + + printf ("%s: sendmmsg returned\n", __FUNCTION__); + + exit (1); +} + +struct cancel_tests tests[] = +{ + ADD_TEST (sendmmsg, 2, 1), +}; +#define ntest_tf (sizeof (tests) / sizeof (tests[0])) + +#include "tst-cancel4-common.c" diff --git a/nptl/tst-cancel4_2.c b/nptl/tst-cancel4_2.c new file mode 100644 index 0000000..e8d7550 --- /dev/null +++ b/nptl/tst-cancel4_2.c @@ -0,0 +1,125 @@ +/* Check recvmmsg cancellation. + + 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; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/ipc.h> +#include <sys/msg.h> +#include <unistd.h> +#include <errno.h> +#include <pthread.h> + +#include "tst-cancel4-common.h" + +static void * +tf_recvmmsg (void *arg) +{ + struct sockaddr_un sun; + + tempfd = socket (AF_UNIX, SOCK_DGRAM, 0); + if (tempfd == -1) + { + printf ("%s: first socket call failed\n", __FUNCTION__); + exit (1); + } + + int tries = 0; + do + { + if (++tries > 10) + { + printf ("%s: too many unsuccessful bind calls\n", __FUNCTION__); + } + + strcpy (sun.sun_path, "/tmp/tst-cancel4-socket-5-XXXXXX"); + if (mktemp (sun.sun_path) == NULL) + { + printf ("%s: cannot generate temp file name\n", __FUNCTION__); + exit (1); + } + + sun.sun_family = AF_UNIX; + } + while (bind (tempfd, (struct sockaddr *) &sun, + offsetof (struct sockaddr_un, sun_path) + + strlen (sun.sun_path) + 1) != 0); + + tempfname = strdup (sun.sun_path); + + tempfd2 = socket (AF_UNIX, SOCK_DGRAM, 0); + if (tempfd2 == -1) + { + printf ("%s: second socket call failed\n", __FUNCTION__); + exit (1); + } + + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + if (arg != NULL) + { + r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__); + exit (1); + } + } + + pthread_cleanup_push (cl, NULL); + + char mem[70]; + struct iovec iov[1]; + iov[0].iov_base = mem; + iov[0].iov_len = arg == NULL ? sizeof (mem) : 0; + + struct mmsghdr mm; + mm.msg_hdr.msg_name = &sun; + mm.msg_hdr.msg_namelen = sizeof (sun); + mm.msg_hdr.msg_iov = iov; + mm.msg_hdr.msg_iovlen = 1; + mm.msg_hdr.msg_control = NULL; + mm.msg_hdr.msg_controllen = 0; + + ssize_t ret = recvmmsg (tempfd2, &mm, 1, 0, NULL); + if (ret == -1 && errno == ENOSYS) + exit (77); + + pthread_cleanup_pop (0); + + printf ("%s: recvmmsg returned\n", __FUNCTION__); + + exit (1); +} + +struct cancel_tests tests[] = +{ + ADD_TEST (recvmmsg, 2, 1), +}; +#define ntest_tf (sizeof (tests) / sizeof (tests[0])) + +#include "tst-cancel4-common.c" |