diff --git a/README.md b/README.md index 2ce51e3..6ea38e4 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,10 @@ The interface consists of four public functions and one variable that reports ho # \brief initializes the job pool # \param[in] pool_size number of parallel jobs allowed # \param[in] echo_command 1 to turn on echo, 0 to turn off + # \param[in] job_pool_function_pre_job (optional) a function that will be called before each job + # \param[in] job_pool_function_post_job (optional) a function that will be called after each job. + # + # To only have a post job function, the given parameter for the pre job function should be: "". function job_pool_init() # \brief waits for all queued up jobs to complete and shuts down the job pool @@ -65,6 +69,8 @@ The interface consists of four public functions and one variable that reports ho This was inspired by [a discussion on StackOverflow](http://stackoverflow.com/questions/6441509/how-to-write-a-process-pool-bash-shell). +For more information on the parameters "job_pool_function_pre_job" and "job_pool_function_post_job" of the "job_pool_init" function, check the sample program "job_pool_sample_injected_functions.sh". + ## `genpass.sh` `genpass.sh` is a random password generator, it will produce a random password of the desired length which is suitable for manual entering via the keyboard. diff --git a/job_pool.sh b/job_pool.sh index 2f801e6..20fc631 100644 --- a/job_pool.sh +++ b/job_pool.sh @@ -45,6 +45,12 @@ job_pool_pool_size=-1 # \brief variable to check for number of non-zero exits job_pool_nerrors=0 +# function that will be called before each job. By default, no function is called. +job_pool_function_pre_job="" + +# function that will be called after each job. By default, no function is called. +job_pool_function_post_job="" + ################################################################################ # private functions ################################################################################ @@ -106,6 +112,11 @@ function _job_pool_worker() # will know we are exiting. echo "${cmd}" >&7 else + # run the pre job injected function if it was provided + if [[ "${job_pool_function_pre_job}" != "" ]]; then + "${job_pool_function_pre_job}" + fi + _job_pool_echo "### _job_pool_worker-${id}: ${cmd}" # run the job { ${cmd} "$@" ; } @@ -124,6 +135,11 @@ function _job_pool_worker() flock --unlock 8 exec 8>&- _job_pool_echo "### _job_pool_worker-${id}: exited ${result}: ${cmd} $@" + + # run the post job injected function if it was provided + if [[ "${job_pool_function_post_job}" != "" ]]; then + "${job_pool_function_post_job}" + fi fi done exec 7>&- @@ -157,12 +173,18 @@ function _job_pool_start_workers() # \brief initializes the job pool # \param[in] pool_size number of parallel jobs allowed # \param[in] echo_command 1 to turn on echo, 0 to turn off +# \param[in] job_pool_function_pre_job (optional) a function that will be called before each job +# \param[in] job_pool_function_post_job (optional) a function that will be called after each job. +# +# To only have a post job function, the given parameter for the pre job function should be: "". function job_pool_init() { local pool_size=$1 local echo_command=$2 # set the global attibutes + job_pool_function_pre_job=$3 + job_pool_function_post_job=$4 job_pool_pool_size=${pool_size:=1} job_pool_echo_command=${echo_command:=0} diff --git a/job_pool_sample_injected_functions.sh b/job_pool_sample_injected_functions.sh new file mode 100755 index 0000000..ca50721 --- /dev/null +++ b/job_pool_sample_injected_functions.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +. job_pool.sh + +##################################################### +# Demonstration of function injection into each job # +##################################################### + +echo "Demonstration of function injection into each job:" + +# sleep some time ($1) then echo something ($2) +function sleep_n_echo() +{ + sleep "$1" + echo "$2" +} + +# Injected function that will be called before each job +# +# Print which worker is starting which job +function print_starting_job() +{ + echo " # _job_pool_worker-${id}: Starting job: ${cmd} $(echo "${args[@]}" | xargs | tr '\v' ' ')" +} + +# Injected function that will be called afetr each job +# +# Kill all workers if the local variable "result" from _job_pool_worker +# indicates that the job failed +function kill_workers() +{ + echo " # _job_pool_worker-${id}: Finished job: ${cmd} $(echo "${args[@]}" | xargs | tr '\v' ' ')" + + # result is undefined in this script, but will be defined when + # the function is injected in _job_pool_worker + if [[ "${result}" != "0" ]]; then + # get the pids of all workers: + # - each worker's process is named after the current script (here, job_pool_sample.sh), + # so we use this name to get the pids + # - we do not include the current script's pid ($$) as it is not a worker, + # (we do not want to kill the script itself, only the workers) + local workers_pids=("$(pgrep -f "$0" | grep -v $$)") + kill ${workers_pids[@]} &> /dev/null & + fi +} + +# allow 3 parallel jobs, and kill all jobs at the first fail using "kill_workers" function +job_pool_init 3 0 print_starting_job kill_workers + +# simulate 3 jobs, where one fails before the others are finished, and interrupts the others +job_pool_run sleep_n_echo 3 a # job 1 +job_pool_run /bin/false # job 2 +job_pool_run sleep_n_echo 3 b # job 3 + +# the job 2 will kill all other running workers, using the function "kill_workers" +# (that is ran after processing each job) + +job_pool_shutdown + +echo -e "\nOnly the failed job exited, the others did not because they were canceled."