#!/bin/bash function main () { local - set +e +m -f declare -- NORM="" declare -- RED="" declare -- GREEN="" declare -- YELLOW="" declare -- CYAN="" declare -- IRED="" declare -- IGREEN="" declare -- IBLUE="" declare -- ICYAN="" declare -- IMAGENTA="" declare -- WHITE="" declare -- quiet="0" declare -- debug="0" declare -i cpus="3" declare -A tasks=([ls]="ls -oF" [del]="rm -rf wfc; echo del wfc recursive" [run]="cd wfc; cargo build --release; cargo run --release & sleep 6 ; kill %1" [mv]="mv -v wavefunctioncollapse-main wfc" [wget]="wget https://github.com/brianfeaster/wavefunctioncollapse/archive/refs/heads/main.zip" [rm]="rm -v main.zip; echo deleted main.zip" [zip]="unzip -o main.zip" ) declare -A taskStates=([ls]="ready" [del]="ready" [run]="ready" [mv]="ready" [wget]="ready" [rm]="ready" [zip]="ready" ) declare -a dependencytree=([0]="* 1 " [1]="+ wget zip mv ls 2 del " [2]="* rm run ") scheduler } scheduler () { local pid2task=() errnum=0; while ((${#pid2task[*]} < cpus)) && spawnNextFree 0; do :; done; while waitForATask; do while ((${#pid2task[*]} < cpus)) && spawnNextFree 0; do :; done; done; ((debug)) && prettyPrintEverything; return $errnum } spawnNextFree () { if [ "${1//[0-9]}" == "" ]; then local depth=$1 taskId allFinished=1; case ${dependencytree[depth]%% *} in "*") for taskId in ${dependencytree[depth]#* }; do spawnNextFree $taskId; case $? in 0) return 0 ;; 2) ;; *) allFinished=0 ;; esac; done; return $((allFinished ? 2 : 1)) ;; "+") for taskId in ${dependencytree[depth]#* }; do spawnNextFree $taskId; case $? in 2) ;; *) return $? ;; esac; done; return 2 ;; *) printf "$IRED[EXCEPTION unknown dependency op '$p'] $NORM"; prettyPrintGraph; exit 4 ;; esac; else local taskId=$1; case ${taskStates[$taskId]} in ready) spawnTask; return 0 ;; running) return 1 ;; finished) return 2 ;; failed | *) return 3 ;; esac; fi } spawnTask () { local taskCmd="${tasks[$taskId]}"; if ((quiet)); then eval "$taskCmd" & else eval "{ $taskCmd ; } 2> >(while read -r l; do echo \"$RED[$BASHPID:$taskId]$NORM \$l\"; done) 1> >(while read -r l; do echo \"$GREEN[$BASHPID:$taskId]$NORM \$l\"; done)" & fi; pid2task[$!]=$taskId; taskStates[$taskId]=running; ((debug)) && echo "$IMAGENTA[Spawned $!:$taskId]$NORM $taskCmd"; return 0 } waitForATask () { local pid taskId taskCmd; ((${#pid2task[@]})) || return 255; ((debug)) && echo $( echo -n "$IMAGENTA[Waiting on ${#pid2task[@]} tasks]$NORM ${!pid2task[@]} " ((2 <= debug)) && prettyPrintGraph ); wait -p pid -n ${!pid2task[@]}; errnum=$?; taskId=${pid2task[$pid]}; taskCmd=${tasks[$taskId]}; unset pid2task[$pid]; if ((errnum)); then taskStates[$taskId]=failed; reportTaskFail; return $errnum; else taskStates[$taskId]=finished; ((debug)) && reportTaskCompleted; return 0; fi } prettyPrintEverything () { echo -e "${WHITE}--TASKS--------$NORM"; for k in $(sort <(tr \ \\n <<<${!tasks[@]})); do echo " $GREEN$k $YELLOW${taskStates[$k]} $NORM${tasks[$k]}"; done; echo -ne "${WHITE}TREE$NORM\n "; prettyPrintGraph } prettyPrintGraph () { local buff=$(_prettyPrintGraph 0); echo $buff } _prettyPrintGraph () { if [ "${1//[0-9]}" == "" ]; then local a w="("; for a in ${dependencytree[$1]}; do printf "$w"; _prettyPrintGraph $a; w=" "; done; printf ")"; else case ${taskStates[$1]} in ready) printf "$1" ;; running) printf "$IGREEN$1$NORM" ;; finished) printf "$IBLUE$1$NORM" ;; failed) printf "$IRED$1$NORM" ;; *) printf "$1" ;; esac; fi } reportTaskFail () { echo "$IRED[Exception pid $pid:$taskId returned $errnum]$NORM $taskCmd" } reportTaskCompleted () { echo $( echo -n "$IGREEN[Completed $pid:$taskId]$NORM $taskCmd " ((2 <= debug)) && prettyPrintGraph ) } main