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