mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-22 10:50:07 +00:00
nptl: Add sendmmsg and recvmmsg cancellation tests
This patch adds cancellation tests for both sendmmsg and recvmmsg syscalls. Since for some system configuration (x86_64/i686 on older kernels and non-Linux platforms), the tests are added as two independent that report as unsupported if the syscall is not presented. Both new tests uses the already tst-cancel4.c code, which as moved to a common tst-cancel4-common{.c,h} files. Tested on x86_64 and i686. * 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.
This commit is contained in:
parent
2c41b52901
commit
b39b6e0c90
10
ChangeLog
10
ChangeLog
@ -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>
|
2016-06-13 Florian Weimer <fweimer@redhat.com>
|
||||||
|
|
||||||
[BZ #20248]
|
[BZ #20248]
|
||||||
|
@ -255,7 +255,8 @@ tests = tst-typesizes \
|
|||||||
tst-tls1 tst-tls2 \
|
tst-tls1 tst-tls2 \
|
||||||
tst-fork1 tst-fork2 tst-fork3 tst-fork4 \
|
tst-fork1 tst-fork2 tst-fork3 tst-fork4 \
|
||||||
tst-atfork1 \
|
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-cancel6 tst-cancel7 tst-cancel8 tst-cancel9 tst-cancel10 \
|
||||||
tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \
|
tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \
|
||||||
tst-cancel16 tst-cancel17 tst-cancel18 tst-cancel19 tst-cancel20 \
|
tst-cancel16 tst-cancel17 tst-cancel18 tst-cancel19 tst-cancel20 \
|
||||||
|
257
nptl/tst-cancel4-common.c
Normal file
257
nptl/tst-cancel4-common.c
Normal file
@ -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"
|
77
nptl/tst-cancel4-common.h
Normal file
77
nptl/tst-cancel4-common.h
Normal file
@ -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 }
|
@ -19,23 +19,21 @@
|
|||||||
/* NOTE: this tests functionality beyond POSIX. POSIX does not allow
|
/* NOTE: this tests functionality beyond POSIX. POSIX does not allow
|
||||||
exit to be called more than once. */
|
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 <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <termios.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/msg.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/uio.h>
|
|
||||||
#include <sys/un.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/poll.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include "pthreadP.h"
|
#include "pthreadP.h"
|
||||||
@ -62,62 +60,17 @@
|
|||||||
aio_suspend() is tested in tst-cancel17.
|
aio_suspend() is tested in tst-cancel17.
|
||||||
|
|
||||||
clock_nanosleep() is tested in tst-cancel18.
|
clock_nanosleep() is tested in tst-cancel18.
|
||||||
|
|
||||||
|
Linux sendmmsg and recvmmsg are checked in tst-cancel4_1.c and
|
||||||
|
tst-cancel4_2.c respectively.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Pipe descriptors. */
|
#include "tst-cancel4-common.h"
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef IPC_ADDVAL
|
#ifndef IPC_ADDVAL
|
||||||
# define IPC_ADDVAL 0
|
# define IPC_ADDVAL 0
|
||||||
#endif
|
#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 *
|
static void *
|
||||||
tf_read (void *arg)
|
tf_read (void *arg)
|
||||||
@ -1391,7 +1344,6 @@ tf_recvmsg (void *arg)
|
|||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
tf_open (void *arg)
|
tf_open (void *arg)
|
||||||
{
|
{
|
||||||
@ -2196,15 +2148,8 @@ tf_msgsnd (void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct
|
struct cancel_tests tests[] =
|
||||||
{
|
{
|
||||||
const char *name;
|
|
||||||
void *(*tf) (void *);
|
|
||||||
int nb;
|
|
||||||
int only_early;
|
|
||||||
} tests[] =
|
|
||||||
{
|
|
||||||
#define ADD_TEST(name, nbar, early) { #name, tf_##name, nbar, early }
|
|
||||||
ADD_TEST (read, 2, 0),
|
ADD_TEST (read, 2, 0),
|
||||||
ADD_TEST (readv, 2, 0),
|
ADD_TEST (readv, 2, 0),
|
||||||
ADD_TEST (select, 2, 0),
|
ADD_TEST (select, 2, 0),
|
||||||
@ -2249,242 +2194,4 @@ static struct
|
|||||||
};
|
};
|
||||||
#define ntest_tf (sizeof (tests) / sizeof (tests[0]))
|
#define ntest_tf (sizeof (tests) / sizeof (tests[0]))
|
||||||
|
|
||||||
|
#include "tst-cancel4-common.c"
|
||||||
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"
|
|
||||||
|
127
nptl/tst-cancel4_1.c
Normal file
127
nptl/tst-cancel4_1.c
Normal file
@ -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"
|
125
nptl/tst-cancel4_2.c
Normal file
125
nptl/tst-cancel4_2.c
Normal file
@ -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"
|
Loading…
Reference in New Issue
Block a user