5

I thought this would give me uninterrupted begin-end pairs, but it doesn't:

#!/bin/bash
fun()(
    flock 1 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
    echo "$BASHPID begin"
    sleep 1;
    echo "$BASHPID end"
)
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
wait

What am I doing wrong?

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
Petr Skocik
  • 28,176
  • 14
  • 81
  • 141

3 Answers3

4

This approach works:

fun()(
  (flock 9 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
   echo "$BASHPID begin"
   sleep 1;
   echo "$BASHPID end"
  ) 9>test
)

This ensures the file on which the lock is held isn't closed as long as the commands which need to be protected haven't finished. (Obviously you should replace test with something more appropriate, e.g. using mktemp.)

Stephen Kitt
  • 411,918
  • 54
  • 1,065
  • 1,164
3

The reason for the failure can be found in man 2 flock:

Locks created by flock() are associated with an open file description (see open(2)). This means that duplicate file descriptors (created by, for example, fork(2) or dup(2)) refer to the same lock, and this lock may be modified or released using any of these descriptors.

This means that because all your processes inherit the same file descriptor, when one of them does a lock, they all share it. And locking the same file descriptor twice is a no-op.

My usual solution to stuff like this is to lock the script itself (though this does pose problems if you run the script multiple times simultaneously).

#!/bin/bash
fun()(
    exec 3<"$0"
    flock 3 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
    echo "$BASHPID begin"
    sleep 1;
    echo "$BASHPID end"
)
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
wait
Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
phemmer
  • 70,657
  • 19
  • 188
  • 223
0

I think the point is:

  • file descriptor 1 is reserved for stdout and already used. we should use other than stdin/out/err like 9 as man 1 flock example shows.

  • flock with file descriptor needs to open lock-file with the specified file descriptor number as man 1 flock EXAMPLE shows as follows:

    (
      flock -n 9 || exit 1
      # ... commands executed under lock ...
    ) 9>/var/lock/mylockfile
    

As the result, the answer is as @Stephen Kitt's.

Fumisky Wells
  • 599
  • 4
  • 6