#!/bin/bash

# slightly modified implementation of sub-process limiter
# by paxdiablo. See http://stackoverflow.com/a/1685440

# bgxupdate - update active processes in a group.
#   Works by transferring each process to new group
#   if it is still active.
# in:  bgxgrp - current group of processes.
# out: bgxgrp - new group of processes.
# out: bgxcount - number of processes in new group.

bgxupdate() {
    bgxoldgrp=${bgxgrp}
    bgxgrp=""
    ((bgxcount = 0))
    bgxjobs=" $(jobs -pr | tr '\n' ' ')"
    for bgxpid in ${bgxoldgrp} ; do
        echo "${bgxjobs}" | grep " ${bgxpid} " >/dev/null 2>&1
        if [[ $? -eq 0 ]] ; then
            bgxgrp="${bgxgrp} ${bgxpid}"
            ((bgxcount = bgxcount + 1))
        fi
    done
}

# bgxlimit - start a sub-process with a limit.
#   Loops, calling bgxupdate until there is a free
#   slot to run another sub-process. Then runs it
#   an updates the process group.
# in:  $1     - the limit on processes.
# in:  $2+    - the command to run for new process.
# in:  bgxgrp - the current group of processes.
# out: bgxgrp - new group of processes

bgxlimit() {
    bgxmax=$1 ; shift
    bgxupdate
    while [[ ${bgxcount} -ge ${bgxmax} ]] ; do
        sleep 0.1
        bgxupdate
    done
    if [[ "$1" != "-" ]] ; then
        "$@" &
        bgxgrp="${bgxgrp} $!"
    fi
    # echo '[' ${bgxgrp} ']'
}

# bgxwait - wait for sub-processes to finish
#   Loops calling bgxupdate until there is something
#   in a process group

bgxwait() {
    bgxupdate
    while [[ ${bgxcount} -ne 0 ]] ; do
        oldcount=${bgxcount}
        while [[ ${oldcount} -eq ${bgxcount} ]] ; do
            sleep 0.1
            bgxupdate
        done
        # echo '[' ${bgxgrp} ']'
    done
}

bgxtest() {
    callback() {
        id=$1 ; shift
        echo start $id $(date | awk '{print $4}') "$BASHPID"
        sleep ${id}0
        echo end $id $(date | awk '{print $4}') "$BASHPID"
    }

    # Test program, create group and run 6 sleeps with
    #   limit of 3.

    bgxgrp=""
    for i in 1 2 3 4 5 6 ; do
        bgxlimit 3 callback ${i}
    done

    # Wait until all others are finished.
    bgxwait;
}
