diff --git a/NEWS b/NEWS index 6718ede09..73c1533c4 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ Any uppercase BUG_* names are modernish shell bug IDs. - The printf built-in command now supports a -v option as on bash and zsh. This allows you to assign formatted output directly to a variable. +- Fixed a performance regression introduced on 2021-05-03 that caused + the shbench[*] fibonacci benchmark to run slower. + [*]: https://github.com/ksh-community/shbench + 2021-11-16: - By default, arithmetic expressions in ksh no longer interpret a number diff --git a/src/cmd/ksh93/include/variables.h b/src/cmd/ksh93/include/variables.h index 3c55d4260..d7219bb3a 100644 --- a/src/cmd/ksh93/include/variables.h +++ b/src/cmd/ksh93/include/variables.h @@ -35,6 +35,7 @@ struct rand int32_t rand_last; }; extern void sh_reseed_rand(struct rand *); +extern void sh_save_rand_seed(struct rand *, int); /* The following defines must be kept synchronous with shtab_variables[] in data/variables.c */ diff --git a/src/cmd/ksh93/sh/init.c b/src/cmd/ksh93/sh/init.c index bc89a2f44..4543dda93 100644 --- a/src/cmd/ksh93/sh/init.c +++ b/src/cmd/ksh93/sh/init.c @@ -645,12 +645,13 @@ static Sfdouble_t nget_seconds(register Namval_t* np, Namfun_t *fp) } /* - * These three functions are used to get and set the RANDOM variable + * These four functions are used to get and set the RANDOM variable */ static void put_rand(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { struct rand *rp = (struct rand*)fp; register long n; + sh_save_rand_seed(rp, 0); if(!val) { fp = nv_stack(np, NIL(Namfun_t*)); @@ -677,7 +678,7 @@ static Sfdouble_t nget_rand(register Namval_t* np, Namfun_t *fp) { struct rand *rp = (struct rand*)fp; register long cur, last= *np->nvalue.lp; - NOT_USED(fp); + sh_save_rand_seed(rp, 1); do cur = (rand_r(&rp->rand_seed)>>rand_shift)&RANDMASK; while(cur==last); diff --git a/src/cmd/ksh93/sh/subshell.c b/src/cmd/ksh93/sh/subshell.c index 2f6726faa..72b38764a 100644 --- a/src/cmd/ksh93/sh/subshell.c +++ b/src/cmd/ksh93/sh/subshell.c @@ -100,6 +100,9 @@ static struct subshell int subdup; char subshare; char comsub; + unsigned int rand_seed; /* parent shell $RANDOM seed */ + int rand_last; /* last random number from $RANDOM in parent shell */ + int rand_state; /* 0 means sp->rand_seed hasn't been set, 1 is the opposite */ #if _lib_fchdir int pwdfd; /* file descriptor for PWD */ char pwdclose; @@ -193,6 +196,17 @@ void sh_subfork(void) { /* this is the child part of the fork */ sh_onstate(SH_FORKED); + /* + * $RANDOM is only reseeded when it's used in a subshell, so if $RANDOM hasn't + * been reseeded yet set rp->rand_last to -2. This allows sh_save_rand_seed() + * to reseed $RANDOM later. + */ + if(!sp->rand_state) + { + struct rand *rp; + rp = (struct rand*)RANDNOD->nvfun; + rp->rand_last = -2; + } subshell_data = 0; shp->subshell = 0; shp->comsub = 0; @@ -231,6 +245,24 @@ int nv_subsaved(register Namval_t *np, int flags) return(0); } +/* + * Save the current $RANDOM seed and state, then reseed $RANDOM. + */ +void sh_save_rand_seed(struct rand *rp, int reseed) +{ + struct subshell *sp = subshell_data; + if(!sh.subshare && sp && !sp->rand_state) + { + sp->rand_seed = rp->rand_seed; + sp->rand_last = rp->rand_last; + sp->rand_state = 1; + if(reseed) + sh_reseed_rand(rp); + } + else if(reseed && rp->rand_last == -2) + sh_reseed_rand(rp); +} + /* * This routine will make a copy of the given node in the * layer created by the most recent virtual subshell if the @@ -467,9 +499,6 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub) struct sh_scoped savst; struct dolnod *argsav=0; int argcnt; - struct rand *rp; /* current $RANDOM discipline function data */ - unsigned int save_rand_seed; /* parent shell $RANDOM seed */ - int save_rand_last; /* last random number from $RANDOM in parent shell */ memset((char*)sp, 0, sizeof(*sp)); sfsync(shp->outpool); sh_sigcheck(shp); @@ -580,11 +609,6 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub) sp->cpipe = shp->cpipe[1]; shp->cpid = 0; sh_sigreset(0); - /* save the current $RANDOM seed and state; reseed $RANDOM */ - rp = (struct rand*)RANDNOD->nvfun; - save_rand_seed = rp->rand_seed; - save_rand_last = rp->rand_last; - sh_reseed_rand(rp); } jmpval = sigsetjmp(buff.buff,0); if(jmpval==0) @@ -748,6 +772,7 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub) if(!shp->subshare) /* restore environment if saved */ { int n; + struct rand *rp; shp->options = sp->options; /* Clean up subshell hash table. */ if(sp->strack) @@ -841,8 +866,11 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub) shp->coutpipe = sp->coutpipe; /* restore $RANDOM seed and state */ rp = (struct rand*)RANDNOD->nvfun; - srand(rp->rand_seed = save_rand_seed); - rp->rand_last = save_rand_last; + if(sp->rand_state) + { + srand(rp->rand_seed = sp->rand_seed); + rp->rand_last = sp->rand_last; + } } shp->subshare = sp->subshare; shp->subdup = sp->subdup; diff --git a/src/cmd/ksh93/tests/variables.sh b/src/cmd/ksh93/tests/variables.sh index 45a5defde..13b83535b 100755 --- a/src/cmd/ksh93/tests/variables.sh +++ b/src/cmd/ksh93/tests/variables.sh @@ -99,6 +99,19 @@ do : $( : $RANDOM $RANDOM $RANDOM ) done [[ $got == "$exp" ]] || err_exit 'Using $RANDOM in subshell influences reproducible sequence in parent environment' \ "(expected $(printf %q "$exp"), got $(printf %q "$got"))" +# Forking a subshell shouldn't throw away the $RANDOM seed in the main shell +exp=$(ulimit -t unlimited; RANDOM=123; echo $RANDOM) +RANDOM=123 +(ulimit -t unlimited; true) +got=${ echo $RANDOM ;} +[[ $got == "$exp" ]] || err_exit "Forking a subshell resets the parent shell's \$RANDOM seed" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" +# Similarly, forking a subshell shouldn't throw away a seed +# previously set inside of the subshell +exp=$(ulimit -t unlimited; RANDOM=789; echo $RANDOM) +got=$(RANDOM=789; ulimit -t unlimited; echo $RANDOM) +[[ $got == "$exp" ]] || err_exit "Forking a subshell resets the subshell's \$RANDOM seed" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" unset N i rand1 rand2 # SECONDS