xref: /freebsd/tools/test/stress2/misc/poll2.sh (revision 06c3fb27)
1#!/bin/sh
2
3#
4# SPDX-License-Identifier: BSD-2-Clause
5#
6# Copyright (c) 2019 Dell EMC Isilon
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27# SUCH DAMAGE.
28#
29
30# Test of pipe_poll()
31# https://reviews.freebsd.org/D21333
32
33# No problems seen.
34
35# markj@ write:
36# A simplified reproducible might be tricky to come up with.  I think this
37# would do it:
38#
39# - Thread W writes 8KB (PIPE_MINDIRECT) of data to a pipe at a time.
40# - Thread P poll()s the pipe for POLLIN.
41# - Thread R reads 8KB of data from the pipe at a time.
42#
43# Thread P uses non-blocking poll() (timeout == 0).  When thread P does
44# not see POLLIN, it signals the reader and the writer and continues
45# polling in a loop.  When thread P sees POLLIN it signals the reader and
46# sleeps until the reader returns and wakes it up.  After threads R and W
47# finish their respective system calls, they always wait for another
48# signal from P before doing anything.
49#
50# Basically, if all three threads are executing their respective system
51# calls, and the reader has drained the writer's data and awoken the
52# writer, there is a window where poll() will return POLLIN even though
53# all data has been read.  If the reader then attempts to read() from the
54# pipe again, it will block and the application appears to be hung.
55
56. ../default.cfg
57
58dir=/tmp
59odir=`pwd`
60cd $dir
61rm -f $dir/poll2.c || exit 1
62sed '1,/^EOF/d' < $odir/$0 > $dir/poll2.c
63mycc -o poll2 -Wall -Wextra -O0 -g poll2.c -lpthread || exit 1
64
65cpuset -l 0 $dir/poll2
66s=$?
67pkill swap
68wait
69
70rm -rf poll2 poll2.c poll2.core
71exit $s
72
73EOF
74#include <sys/param.h>
75
76#include <err.h>
77#include <errno.h>
78#include <fcntl.h>
79#include <poll.h>
80#include <pthread.h>
81#include <pthread_np.h>
82#include <stdio.h>
83#include <stdlib.h>
84#include <time.h>
85#include <unistd.h>
86
87static volatile int done, frd, fwr, fpl;
88static int fds[2];
89static char b1[8192], b2[8192];
90
91#define RUNTIME (2 * 60)
92#define LOOP 400000
93
94static void *
95wr(void *data __unused)
96{
97	int i;
98
99	for (i = 0; i < LOOP; i++) {
100		pthread_set_name_np(pthread_self(), "wr-idle");
101		while (fwr == 0)
102			usleep(5);
103		pthread_set_name_np(pthread_self(), "wr-act");
104		fpl = 1;
105		if (write(fds[1], b1, sizeof(b1)) != sizeof(b1))
106			err(1, "write");
107		fpl = 1;
108		fwr = 0;
109	}
110
111	return (NULL);
112}
113
114static void *
115rd(void *data __unused)
116{
117	int i;
118
119	for (i = 0; i < LOOP; i++) {
120		fpl = 1;
121		pthread_set_name_np(pthread_self(), "rd-idle");
122		while (frd == 0)
123			usleep(5);
124		pthread_set_name_np(pthread_self(), "rd-act");
125		if (read(fds[0], b2, sizeof(b2)) != sizeof(b2))
126			err(1, "read");
127		frd = 0;
128		fpl = 1;
129	}
130	done = 1;
131
132	return (NULL);
133}
134
135static void *
136pl(void *data __unused)
137{
138	struct pollfd pfd;
139	int r;
140
141	pfd.fd = fds[0];
142	pfd.events = POLLIN;
143	while (done == 0) {
144		pfd.fd = fds[0];
145		pfd.events = POLLIN;
146		pthread_set_name_np(pthread_self(), "pl-idle");
147		pthread_set_name_np(pthread_self(), "pl-act");
148		while (fpl == 0)
149			usleep(5);
150again:
151		if ((r = poll(&pfd, 1, 0)) == -1)
152			err(1, "poll");
153		if (done == 1)
154			return (NULL);
155		if (r == 0) {
156			frd = fwr = 1;
157			goto again;
158		} else {
159			fpl = 0;
160			frd = fwr = 1;
161		}
162	}
163
164	return (NULL);
165}
166
167void
168test(void)
169{
170	pthread_t tid[3];
171	int rc;
172
173	if (pipe(fds) == -1)
174		err(1, "pipe");
175	done = 0;
176	fpl = 0;
177	frd = 0;
178	fwr = 0;
179	if ((rc = pthread_create(&tid[0], NULL, rd, NULL)) != 0)
180		errc(1, rc, "pthread_create");
181	if ((rc = pthread_create(&tid[1], NULL, wr, NULL)) != 0)
182		errc(1, rc, "pthread_create");
183	if ((rc = pthread_create(&tid[2], NULL, pl, NULL)) != 0)
184		errc(1, rc, "pthread_create");
185
186	frd = 1;
187	fwr = 1;
188
189	if ((rc = pthread_join(tid[0], NULL)) != 0)
190		errc(1, rc, "pthread_join");
191	if ((rc = pthread_join(tid[1], NULL)) != 0)
192		errc(1, rc, "pthread_join");
193	if ((rc = pthread_join(tid[2], NULL)) != 0)
194		errc(1, rc, "pthread_join");
195
196	close(fds[0]);
197	close(fds[1]);
198}
199
200int
201main(void)
202{
203	time_t start;
204
205	alarm(600);
206	start = time(NULL);
207	while ((time(NULL) - start) < RUNTIME) {
208		test();
209	}
210
211	return (0);
212}
213