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>
|
||||
|
||||
[BZ #20248]
|
||||
|
@ -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 \
|
||||
|
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
|
||||
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 <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/msg.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.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 "pthreadP.h"
|
||||
@ -62,62 +60,17 @@
|
||||
aio_suspend() is tested in tst-cancel17.
|
||||
|
||||
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. */
|
||||
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;
|
||||
|
||||
#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
|
||||
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 (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"
|
||||
|
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