#!/bin/sh

debug=true

error() {
	echo "error: $*" >&2
	exit 1
}

parallelize_list=" "

# 1: max-parallel
queue_full() {
	queue_full_max="$1"
	set -- $parallelize_list
	if [ $# -lt $queue_full_max ]; then
		$debug && echo "can queue more" >&2
		return 1
	elif [ $# -eq $queue_full_max ]; then
		$debug && echo "queue full:${parallelize_list% }" >&2
		return 0
	else
		error "more jobe queued ($#) than max ($queue_full_max)"
	fi
}

update_queue() {
	set -- $parallelize_list
	$debug && echo "updating queue (size $#)" >&2
	while [ $# -gt 0 ]; do
		if ! kill -0 $1 2>/dev/null; then
			$debug && echo "waiting for $1"
			wait $1
			$debug && echo "finished: $1"
			parallelize_list="${parallelize_list% $1 *} ${parallelize_list#* $1 }"
		fi
		shift
	done
	if $debug; then
		set -- $parallelize_list
		echo "updated queue: size $#" >&2
	fi
}

# 1: func, 2: arg
queue_job() {
	$debug && echo "queueing $2" >&2
	"$1" "$2" &
	parallelize_list="$parallelize_list$! "
	$debug && echo "queued $2: $!"
}

# 1: function, 2: max-parallel, 3...: instances
parallelize() {
	parallelize_func="$1"; shift
	parallelize_max="$1"; shift
	while [ $# -gt 0 ]; do
		while queue_full $parallelize_max; do
			sleep 1
			update_queue
		done
		queue_job "$parallelize_func" "$1"; shift
	done
}

func() {
	echo "func: $1"
	sleep 5
	echo "func $1: done"
}

parallelize func "$@"
