1######################################################################## 2# # 3# This software is part of the ast package # 4# Copyright (c) 1982-2013 AT&T Intellectual Property # 5# and is licensed under the # 6# Eclipse Public License, Version 1.0 # 7# by AT&T Intellectual Property # 8# # 9# A copy of the License is available at # 10# http://www.eclipse.org/org/documents/epl-v10.html # 11# (with md5 checksum b35adb5213ca9657e911e9befb180842) # 12# # 13# Information and Software Systems Research # 14# AT&T Research # 15# Florham Park NJ # 16# # 17# David Korn <dgkorn@gmail.com> # 18# # 19######################################################################## 20 21float DELAY=0.2 22integer FOREGROUND=10 BACKGROUND=2 23 24s=$($SHELL -c ' 25integer i foreground=0 background=0 26float delay='$DELAY' d=0 s=0 27 28set --errexit 29 30trap "(( background++ ))" CHLD 31 32(( d = delay )) 33for ((i = 0; i < '$BACKGROUND'; i++)) 34do 35 sleep $d & 36 (( d *= 4 )) 37 (( s += d )) 38done 39 40for ((i = 0; i < '$FOREGROUND'; i++)) 41do 42 (( foreground++ )) 43 sleep $delay 44 (( s -= delay )) 45 $SHELL -c : > /dev/null # foreground does not generate SIGCHLD 46done 47 48if (( (s += delay) < 1 )) 49then 50 (( s = 1 )) 51fi 52 53sleep $s 54wait 55print foreground=$foreground background=$background 56') || log_error "test loop failed" 57 58eval $s 59 60(( foreground == FOREGROUND )) || log_error "expected '$FOREGROUND foreground' -- got '$foreground' (DELAY=$DELAY)" 61(( background == BACKGROUND )) || log_error "expected '$BACKGROUND background' -- got '$background' (DELAY=$DELAY)" 62 63set --noerrexit 64 65if [[ ${.sh.version} == Version?*([[:upper:]])J* ]] 66then 67 jobmax=4 68 got=$($SHELL -c ' 69 JOBMAX='$jobmax' JOBCOUNT=$(('$jobmax'*2)) 70 integer running=0 maxrunning=0 71 trap "((running--))" CHLD 72 for ((i=0; i<JOBCOUNT; i++)) 73 do 74 sleep 1 & 75 sleep .1 76 if ((++running > maxrunning)) 77 then 78 ((maxrunning=running)) 79 fi 80 81 done 82 83 wait 84 print running=$running maxrunning=$maxrunning 85 ') 86 exp='running=0 maxrunning='$jobmax 87 [[ $got == $exp ]] || log_error "SIGCHLD trap queueing failed -- expected '$exp', got '$got'" 88 89 got=$($SHELL -c ' 90 typeset -A proc 91 92 trap " 93 print \${proc[\$!].name} \${proc[\$!].status} \$? 94 unset proc[\$!] 95 " CHLD 96 97 { sleep 3; print a; exit 1; } & 98 proc[$!]=( name=a status=1 ) 99 100 { sleep 2; print b; exit 2; } & 101 proc[$!]=( name=b status=2 ) 102 103 { sleep 1; print c; exit 3; } & 104 proc[$!]=( name=c status=3 ) 105 106 while (( ${#proc[@]} )) 107 do 108 sleep -s 109 done 110 ') 111 exp='c\nc 3 3\nb\nb 2 2\na\na 1 1' 112 [[ $got == $exp ]] || log_error "SIGCHLD trap queueing failed -- expected $(printf %q "$exp"), got $(printf %q "$got")" 113fi 114 115{ 116got=$( ( sleep 1;print $'\n') | $SHELL -c 'function handler { : ;} 117 trap handler CHLD; sleep .3 & IFS= read; print good') 118} 2> /dev/null 119[[ $got == good ]] || log_error 'SIGCLD handler effects read behavior' 120 121set -- $( 122 ( 123 $SHELL -xc $' 124 trap \'wait $!; print $! $?\' CHLD 125 { sleep 0.1; exit 9; } & 126 print $! 127 sleep 0.5 128 ' 129 ) 2>/dev/null; print $? 130) 131if (( $# != 4 )) 132then 133 log_error "CHLD trap failed -- expected 4 args, got $#" 134elif (( $4 != 0 )) 135then 136 log_error "CHLD trap failed -- exit code $4" 137elif (( $1 != $2 )) 138then 139 log_error "child pid mismatch -- got '$1' != '$2'" 140elif (( $3 != 9 )) 141then 142 log_error "child status mismatch -- expected '9', got '$3'" 143fi 144 145trap '' CHLD 146integer d 147for ((d=0; d < 2000; d++)) 148do 149 if print foo | grep bar 150 then 151 break 152 fi 153done 154 155(( d==2000 )) || log_error "trap '' CHLD causes side effects d=$d" 156trap - CHLD 157 158x=$($SHELL 2> /dev/null -ic '/bin/notfound; sleep .5 & sleep 1;jobs') 159[[ $x == *Done* ]] || log_error 'SIGCHLD blocked after notfound' 160x=$($SHELL 2> /dev/null -ic 'kill -0 12345678901234567876; sleep .5 & sleep 1;jobs') 161[[ $x == *Done* ]] || log_error 'SIGCHLD blocked after error message' 162print 'set -o monitor;sleep .5 & sleep 1;jobs' > $TEST_DIR/foobar 163chmod +x $TEST_DIR/foobar 164x=$($SHELL -c "echo | $TEST_DIR/foobar") 165[[ $x == *Done* ]] || log_error 'SIGCHLD blocked for script at end of pipeline' 166 167tmpfile=$TEST_DIR/file 168$SHELL > $tmpfile <<- \EOF 169 trap 'printf "%d %d %s\n" .sh.sig.pid $! "${.sh.sig.code}"' CHLD 170 { 171 for ((i=0 ; i < 10 ; i++ )) ; do 172 sleep .4 173 done 174 175 } & 176 cpid=$! 177 sleep .2 & 178 print $cpid $! 179 sleep 1 180 kill -STOP $cpid 181 sleep 1 182 kill -CONT $cpid 183 sleep 1 184 wait 185EOF 186 187# Check the output of the previous code to determine if the platform supports reporting SIGCONT via 188# waitpid() or wait4(). Ideally this would be done in a manner that doesn't result in false 189# negatives but it is not obvious how to do that. See issue #561. 190if grep -q CONTINUED $tmpfile 191then 192 expected_statuses=(EXITED STOPPED CONTINUED EXITED) 193else 194 # The platform does not support reporting SIGCONT via waitpid() or wait4(). 195 expected_statuses=(EXITED STOPPED EXITED) 196fi 197 198{ 199 read xpid pid 200 for stat in $expected_statuses 201 do 202 read pid1 pid2 status || { log_error "line with stopped continued or exited expected";break;} 203 [[ $pid1 == $pid ]] || log_error ".sh.sig.pid=$pid1 should be $pid" 204 [[ $pid2 == $pid ]] || log_error "\$!=$pid1 should be $pid" 205 [[ $status == $stat ]] || log_error "status is $status, should be $stat" 206 pid=$xpid 207 done 208 209} < $tmpfile 210 211# ========== 212# Verify we can trap the termination signal for every job we put in the background. 213# 214typeset -A pids 215function sighandler_chld 216{ 217 if [[ ${pids[${.sh.sig.pid}]} ]] 218 then 219 unset -v pids[${.sh.sig.pid}] 220 else 221 log_error "${.sh.sig.pid} not recognized as a sleep command put in the background" 222 trap - CHLD 223 fi 224} 225 226trap 'sighandler_chld' CHLD 227integer i 228typeset -i n_jobs=100 229for (( i=1 ; i <= n_jobs ; i++ )) 230do 231 # The multiplication is to slightly stagger the exit time of the sleeps to make it more likely 232 # we'll get a SIGCHLD for each one. 233 sleep $(( i * 0.01 )) & 234 pids[$!]=1 235done 236 237wait 238# 239# We would like to test that $pids is empty. But we can't because most UNIX implementations do not 240# queue new instances of a signal while the handler for the signal is running. This means that it is 241# likely that the SIGCHLD generated by some exiting `sleep` commands above will be dropped on the 242# floor by the kernel. And therefore `sighandler_chld` will not have been run for some of the jobs. 243# But it should be run for the majority of them so verify that is the case. 244# 245# TODO: Decrease the expected value from 85% to 30% or less when issue #735 is fixed. 246expect=$(( n_jobs * 85 / 10 )) 247actual=${#pids[*]} 248(( actual <= expect )) || \ 249 log_error "too many jobs missed by sighandler_chld" "$expect" "$actual $(typeset -p pids)" 250