#! /bin/sh

# Demonstrate use of the bakery algorithm for crash-safe mutual exclusion in a
# shell script.  Sample invocation:
#   (rm -r buildlock; for child in `seq 1 10`; do sh bakery.sh & done; wait)

set -u
PS4='+$$+ '  # include PID in "set -x" messages
sleep=':'  # busy wait, because "sleep .01" is a GNU extension
mkdir -p buildlock/choosing buildlock/number

# print the contents of a file, or "0" if file is missing or empty
lockread() {
  result=`2>/dev/null cat "$1"`
  test -z "$result" && result=0
  echo "$result"
}

for iter in `seq 1 100`; do

# Algorithm and terminology from:
#   A New Solution of Dijkstra's Concurrent Programming Problem Communications
#   of the ACM 17, 8 (August 1974), 453-455.
cd buildlock
echo 1 >choosing/$$
echo "$$ entered the doorway"
max=0
cd number
echo 0 >$$
for pid in *; do
  number=`lockread $pid`
  [ $number -gt $max ] && max=$number
done
my_number=$((1 + $max))
echo $my_number >$$
cd ../choosing
echo 0 >$$
echo "$$ in the bakery, number = $my_number"
for pid in $(printf '%s\n' * | sort -n); do
  while [ `lockread $pid` = 1 ]; do
	2>/dev/null kill -0 "$pid" || { rm -f ../number/$pid; rm -f $pid; }; $sleep
  done
  while :; do
	number=`lockread ../number/$pid`
	[ $number -ne 0 -a \
	  \( $number -lt $my_number -o \
	  $number -eq $my_number -a $pid -lt $$ \) ] || break
	2>/dev/null kill -0 "$pid" || { rm -f ../number/$pid; rm -f $pid; }; $sleep
  done
done
cd ../..
echo "$$ in critical section"
# exit  # simulate crash
echo "$$ leaving critical section"
rm -f buildlock/number/$$
rm -f buildlock/choosing/$$

done
