Obtain exclusive read/write lock on a file for atomic updates
File locks aren't mandatory1 -- i.e., you can't lock a file so that another process cannot access it. Locking a file means that if a(nother) process checks to see if it has been locked, it will know.
The purpose of flock
is to do stuff like what you want, but you must then use flock
for each and every attempted access. Keep in mind those are blocking calls; from man flock
:
if the lock cannot be immediately acquired, flock waits until the lock is available
1. Which makes the feature seem useless if you are using it for, e.g., security, but that is not the purpose of file locks -- they're for synchronization, which is what you are doing. User Leo pointed out that there may be a non standardized implementation of mandatory file locking in the linux kernel (see this discussion), based on historical parallels from other *nix operating systems. However, this looks to be a C level interface only.
Bash's processing of the command below may be surprising:
flock -x -w 5 /dev/shm/counter.txt echo "4" > /dev/shm/counter.txt && sleep 5
Bash first runs flock -x -w 5 /dev/shm/counter.txt echo "4" > /dev/shm/counter.txt
and, if that completes successfully (releasing the lock), then it runs sleep 5
. Thus, the lock is not held for the 5 seconds that one may expect.
Aside on &
versus &&
If one has two commands, A
and B
, then:
A & B
startsA
in the background and then startsB
without waiting forA
to finish.A && B
startsA
, waits for it to complete, and then, if it completes successfully (exit code 0), startsB
. IfA
fails (nonzero exit code), thenB
is never run.
In sum, &
and &&
are two completely different list operators.
You could use "sh -c command..." to run the entire shell command, including file redirection, with the lock held. Also, because you are using the file as a counter, you need to hold the lock continuously while you do the read and the write back. So you would want to do something like this to increment the counter and return its new value:
flock --exclusive --wait 5 /dev/shm/counter.txt sh -c 'read count < /dev/shm/counter.txt ; echo $((count + 1)) > /dev/shm/counter.txt ; echo $((count + 1))'
Changing the plus signs to minus signs should decrement the counter:
flock --exclusive --wait 5 /dev/shm/counter.txt sh -c 'read count < /dev/shm/counter.txt ; echo $((count - 1)) > /dev/shm/counter.txt ; echo $((count - 1))'
I expect you will initialize the counter before any contention can occur, so there should be no need to worry about locking that early:
echo 0 > /dev/shm/counter.txt
I don't think you would ever need to clobber the counter value while contention could occur, but, if you ever did, you should do so thusly:
flock --exclusive --wait 5 /dev/shm/counter.txt sh -c 'echo 0 > /dev/shm/counter.txt'
I think you understand how to do the read, but I include it for completeness:
flock --shared /dev/shm/counter.txt cat /dev/shm/counter.txt