common/bin/hgforest.sh

Print this page
rev 1022 : 8041151: More concurrent hgforest
Reviewed-by: duke

@@ -70,16 +70,25 @@
 usage() {
       echo "usage: $0 [-q|--quiet] [-v|--verbose] [-s|--sequential] [--] <command> [commands...]" > ${status_output}
       exit 1
 }
 
-
 if [ "x" = "x$command" ] ; then
   echo "ERROR: No command to hg supplied!"
   usage
 fi
 
+# Check if we can use fifos for monitoring sub-process completion.
+on_windows=`uname -s | egrep -ic -e 'cygwin|msys'`
+if [ on_windows = "1" ]; then
+  # cygwin has (2014-04-18) broken (single writer only) FIFOs
+  # msys has (2014-04-18) no FIFOs.
+  have_fifos="false"
+else
+  have_fifos="true"
+fi
+
 # Clean out the temporary directory that stores the pid files.
 tmp=/tmp/forest.$$
 rm -f -r ${tmp}
 mkdir -p ${tmp}
 

@@ -208,25 +217,38 @@
       (PYTHONUNBUFFERED=true hg${global_opts} serve -A ${status_output} -E ${status_output} --pid-file ${tmp}/serve.pid --web-conf ${tmp}/serve.web-conf; echo "$?" > ${tmp}/serve.pid.rc ) 2>&1 &
     ) 2>&1 | sed -e "s@^@serve:   @" > ${status_output}
   ) &
 else
   # Run the supplied command on all repos in parallel.
+
+  # n is the number of subprocess started or which might still be running.
   n=0
+  if [ $have_fifos = "true" ]; then
+    # if we have fifos use them to detect command completion.
+    mkfifo ${tmp}/fifo
+    exec 3<>${tmp}/fifo
+    if [ "${sflag}" = "true" ] ; then
+      # force sequential
+      at_a_time=1
+    fi
+  fi
+
   for i in ${repos} ${repos_extra} ; do
     n=`expr ${n} '+' 1`
     repopidfile=`echo ${i} | sed -e 's@./@@' -e 's@/@_@g'`
     reponame=`echo ${i} | sed -e :a -e 's/^.\{1,20\}$/ &/;ta'`
     pull_base="${pull_default}"
     for j in $repos_extra ; do
       if [ "$i" = "$j" ] ; then
           pull_base="${pull_extra}"
       fi
     done
+    pull_base="`echo ${pull_base} | sed -e 's@[/]*$@@'`"
     (
       (
         if [ "${command}" = "clone" -o "${command}" = "fclone" -o "${command}" = "tclone" ] ; then
-          pull_newrepo="`echo ${pull_base}/${i} | sed -e 's@\([^:]/\)//*@\1@g'`"
+          pull_newrepo="${pull_base}/${i}"
           path="`dirname ${i}`"
           if [ "${path}" != "." ] ; then
             times=0
             while [ ! -d "${path}" ]   ## nested repo, ensure containing dir exists
             do

@@ -235,44 +257,64 @@
                 echo "${path} still not created, waiting..." > ${status_output}
               fi
               sleep 5
             done
           fi
-          echo "hg clone ${pull_newrepo} ${i}" > ${status_output}
+          echo "hg${global_opts} clone ${pull_newrepo} ${i}" > ${status_output}
           (PYTHONUNBUFFERED=true hg${global_opts} clone ${pull_newrepo} ${i}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 &
         else
           echo "cd ${i} && hg${global_opts} ${command} ${command_args}" > ${status_output}
           cd ${i} && (PYTHONUNBUFFERED=true hg${global_opts} ${command} ${command_args}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 &
         fi
 
         echo $! > ${tmp}/${repopidfile}.pid
       ) 2>&1 | sed -e "s@^@${reponame}:   @" > ${status_output}
+      if [ $have_fifos = "true" ]; then
+        echo "${reponame}" >&3
+      fi
     ) &
 
-    if [ `expr ${n} '%' ${at_a_time}` -eq 0 -a "${sflag}" = "false" ] ; then
-      sleep 2
-      echo "Waiting 5 secs before spawning next background command." > ${status_output}
-      sleep 3
+    if [ $have_fifos = "true" ]; then
+      # check on count of running subprocesses and possibly wait for completion
+      if [ ${at_a_time} -lt ${n} ] ; then
+        # read will block until there are completed subprocesses
+        while read repo_done; do
+          n=`expr ${n} '-' 1`
+          if [ ${n} -lt ${at_a_time} ] ; then
+            # we should start more subprocesses
+            break;
     fi
-
-    if [ "${sflag}" = "true" ] ; then
+        done <&3
+      fi
+    else
+      if [ "${sflag}" = "false" ] ; then
+        # Compare completions to starts
+        completed="`(ls -1 ${tmp}/*.pid.rc 2> /dev/null | wc -l) || echo 0`"
+        while [ ${at_a_time} -lt `expr ${n} '-' ${completed}` ] ; do
+          # sleep a short time to give time for something to complete
+          sleep 1
+          completed="`(ls -1 ${tmp}/*.pid.rc 2> /dev/null | wc -l) || echo 0`"
+        done
+      else
+        # complete this task before starting another.
         wait
     fi
+    fi
   done
 fi
 
-# Wait for all hg commands to complete
+# Wait for all subprocesses to complete
 wait
 
 # Terminate with exit 0 only if all subprocesses were successful
 ec=0
 if [ -d ${tmp} ]; then
   for rc in ${tmp}/*.pid.rc ; do
     exit_code=`cat ${rc} | tr -d ' \n\r'`
     if [ "${exit_code}" != "0" ] ; then
       repo="`echo ${rc} | sed -e s@^${tmp}@@ -e 's@/*\([^/]*\)\.pid\.rc$@\1@' -e 's@_@/@g'`"
-      echo "WARNING: ${repo} exited abnormally." > ${status_output}
+      echo "WARNING: ${repo} exited abnormally ($exit_code)" > ${status_output}
       ec=1
     fi
   done
 fi
 exit ${ec}