Created
April 25, 2017 22:37
-
-
Save ryran/f074407ac8ea5565388603177a0dbbd8 to your computer and use it in GitHub Desktop.
Simple mandatory file locking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
flock_open() { | |
# Open a blocking request for an exclusive lock against file $1, by | |
# loop-trying a mkdir on: "$(dirname $1)/.$(basename $1).lock" | |
# Lock can be stolen from a dead PID 3.2-19.2 seconds after they die, though | |
# this can be changed by calling flock_open w/custom deadmax= env variable. | |
local parentdir=$(dirname "${1}") | |
local lock=${parentdir}/.${1##*/}.lock~ | |
local owner= lastOwner= | |
local -i deadloops=0 deadmax=${deadmax:-$(shuf -i 8-24 -n1)} | |
# Create parent dir if necessary | |
[[ ${parentdir} != . ]] && mkdir -p "${parentdir}" | |
until mkdir "${lock}" 2>/dev/null; do | |
# Sleep for 0.4sec - 0.8sec | |
usleep $(shuf -i 400000-800000 -n 1) | |
lastOwner=${owner} | |
owner=$(cat "${lock}/owner" 2>/dev/null) | |
if cat /proc/${owner}/fd/77 &>/dev/null; then | |
# Reset deadloops counter if owner is active | |
deadloops=0 | |
else | |
# Log a deadloop if lock owner is dead and pid hasn't changed | |
[[ ${owner} == ${lastOwner} ]] && deadloops+=1 || deadloops=0 | |
fi | |
# After $deadmax number of consecutive dead loops, steal lock | |
((deadloops>=deadmax)) && break | |
done | |
exec 77>"${lock}/owner" | |
echo ${$} >&77 | |
trap "flock_close '${1}'" HUP INT TERM EXIT | |
} | |
flock_close() { | |
local lock=$(dirname "${1}")/.${1##*/}.lock~ | |
exec 77>&- | |
rm -rf "${lock}" | |
trap - HUP INT TERM EXIT | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I had a use-case for mandatory file locking and
flock
just didn't cut it. I have a small number (less than 20) of parallel processes that aren't started at exactly the same time and all read & append (at some point) from/to the same file. I wanted to make sure none of them clobber each other (the programs in use do NOT pay attention to advisory locks in Linux) and I wanted to be able to steal locks from a dead process (if one dies without cleaning up).To use the above, anything that wants to modify
/SOME/FILE
will need to wrap its operations inside calls toflock_open
andflock_close
, e.g.:Caveats:
mkdir
is not guaranteed atomic on NFS.)For simpler use-cases, take a look at
man 1 flock
.