My solution uses two files, pid.temp and data.temp:
backgroundGetData() {
local data=$1
# if global is null, check file.
if [ -z "$data" ]; then
data=$( cat $DATA_TEMP_FILE 2>/dev/null )
fi
# if file is empty, check process is making the request
if [ -z "$data" ]; then
for i in {1..5}; do
echo "INFO - Process: $BASHPID - Attempting to lock data temp file" >&2
local request_pid=$( cat $PID_FILE 2>/dev/null )
if [ -z "$request_pid" ]; then request_pid=0; fi
local exit_code=1
if [ "$request_pid" -eq 0 ]; then
( flock -n 200 || exit 1
echo "INFO - Process: $BASHPID - Fetching data." >&2
echo "$BASHPID">"$PID_FILE"
getData > $DATA_TEMP_FILE
) 200>$DATA_TEMP_FILE
exit_code=$?
fi
echo "INFO - Process: $BASHPID - returned $exit_code from lock attempt.">&2
[ $request_pid -ne 0 ] && echo "INFO - Process: $BASHPID - $request_pid is possibly locking">&2
if [ $exit_code -ne 0 ] && [ $request_pid -ne 0 ]; then
echo "INFO - Process: $BASHPID - waiting on $request_pid to complete">&2
tail --pid=${request_pid} -f /dev/null
echo "INFO - Process: $BASHPID - finished waiting.">&2
break
elif [ $exit_code -eq 0 ]; then break;
else
sleep 2
fi
done
data=$( cat $DATA_TEMP_FILE )
if [ -z "$data" ]; then
echo "WARN - Process: $BASHPID - Failed to retrieve data.">&2
fi
fi
echo "$least_loaded"
}
And it can be used like so:
DATA=""
DATA_TEMP_FILE="data.temp"
PID_FILE="pid.temp"
$( backgroundGetData $DATA ) & ## Begin making request
doThing() {
if [ -z $DATA ]; then
# Redirect 3 to stdout, then use sterr in backgroundGetData to 3 so that
# logging messages can be shown and output can also be captued in variable.
exec 3>&1
DATA=$( backgroundGetData $DATA 2>&3)
fi
}
for job in "$jobs"; do
doThing &
done
It's working for me, though I'm not 100% sure on how safe it is.