This implementation is inspired by the POSIX functions sem_wait()
and sem_post()
. It tries to get rid of busy loops in the code and appears to work after some simple tests that have been done. But code involving concurrency is hard to get right. Hopefully, there are no race conditions and/or deadlocks in it.
Update 1
semInit() was changed a little bit since the last test. The change had a missing semicolon issue causing syntax error which I have just corrected in this update.
#!/bin/bash
## Filename: semaphore.bash.inc
## Blocking other processes by running the function as a
## process to occupy flock semFile.
lkrRun()
{
{
flock -x 5
while :; do
sleep 30
done
} 5>/tmp/semFile
}
semInit()
{
local semValArgInit="$1"
{ [ -z "$semValArgInit" ] || [ $semValArgInit -lt 0 ]; } && semValArgInit=0
echo $semValArgInit >/tmp/semVal
\rm -f /tmp/lkrPid
touch /tmp/auxFile
touch /tmp/semFile
}
semWait()
{
local semValTmp
while :; do
flock -x 5
[ $(cat /tmp/semVal) -eq 0 ] || {
semValTmp=$(cat /tmp/semVal)
let semValTmp--
echo $semValTmp >/tmp/semVal
flock -u 5
break
}
{ [ -f /tmp/lkrPid ] && kill -0 $(cat /tmp/lkrPid); } || {
lkrRun &
echo $! >/tmp/lkrPid
}
flock -u 5
## Block point to prevent processes from busying at looping.
## It should still work without it but just become
## inefficient.
flock -x 6
flock -u 6
done 5>/tmp/auxFile 6>/tmp/semFile
}
semPost()
{
local semValTmp
{
flock -x 5
semValTmp=$(cat /tmp/semVal)
let semValTmp++
echo $semValTmp >/tmp/semVal
## Kill the whole locker's process group to relinquish
## flock semFile and resume processes blocked on it.
[ -f /tmp/lkrPid ] && kill -9 -- -$(cat /tmp/lkrPid)
\rm -f /tmp/lkrPid
} 5>/tmp/auxFile
}