}
mutex_trylock() {
- local lock
-
- lock="$1"
+ local lock="$1"
if ! ln -s "$BASHPID" "$lock" &> /dev/null; then
return 1
}
mutex_lock() {
- local lock
-
- lock="$1"
+ local lock="$1"
while ! mutex_trylock "$lock"; do
+ # We can't inotifywait on symlinks. Which is
+ # fine because when the symlink is removed, the
+ # containing directory is changed. Hence, we can
+ # watch the containing directory instead.
+
if ! inotifywait -qq "${lock%/*}"; then
return 1
fi
}
mutex_unlock() {
- local lock
- local owner
+ local lock="$1"
- lock="$1"
+ local owner
if ! owner=$(readlink "$lock" 2> /dev/null); then
return 1
fi
- if [ "$owner" -ne "$BASHPID" ]; then
+ if (( owner != BASHPID )); then
return 2
fi
- if ! rm -f "$lock"; then
+ if ! rm "$lock"; then
return 3
fi
--- /dev/null
+#!/bin/bash
+
+#
+# wmutex - weak mutex implementation for bash scripts
+# Copyright (C) 2021 - Matthias Kruk <m@m10k.eu>
+#
+
+__init() {
+ return 0
+}
+
+wmutex_trylock() {
+ local lock="$1"
+
+ if ! ln -s "$BASHPID" "$lock" &> /dev/null; then
+ return 1
+ fi
+
+ return 0
+}
+
+wmutex_lock() {
+ local lock="$1"
+
+ while ! wmutex_trylock "$lock"; do
+ if ! inotifywait -qq "${lock%/*}"; then
+ return 1
+ fi
+ done
+
+ return 0
+}
+
+wmutex_unlock() {
+ local lock="$1"
+
+ if ! rm "$lock"; then
+ return 1
+ fi
+
+ return 0
+}
--- /dev/null
+#!/usr/bin/env bats
+
+. toolbox.sh
+include "wmutex"
+
+setup() {
+ delete=()
+
+ return 0
+}
+
+
+teardown() {
+ if (( ${#delete[@]} > 0 )); then
+ echo "${delete[*]}" >> /tmp/teardown.bats
+ rm -f "${delete[@]}"
+ fi
+
+ return 0
+}
+
+@test "wmutex_trylock() returns success if the lock was created" {
+ local name
+
+ name="test_$RANDOM"
+
+ wmutex_trylock "$name"
+ [ -L "$name" ]
+
+ delete+=("$name")
+}
+
+@test "wmutex_trylock() returns failure if the lock was not created" {
+ local name
+
+ name="/$RANDOM/$RANDOM/$RANDOM/$RANDOM"
+
+ ! [ -d "${name%/*}" ]
+ ! wmutex_trylock "$name"
+}
+
+@test "wmutex_lock() returns success if the lock was created" {
+ local name
+
+ name="test_$RANDOM"
+
+ wmutex_lock "$name"
+ [ -L "$name" ]
+
+ delete+=("$name")
+}
+
+@test "wmutex_lock() returns failure if the lock was not created" {
+ local name
+
+ # Unlikely to exist path
+ name="/$RANDOM/$RANDOM/RANDOM/$RANDOM"
+
+ ! [ -d "${name%/*}" ]
+ ! wmutex_lock "$name"
+ ! [ -L "$name" ]
+}
+
+@test "wmutex_unlock() returns success if the lock was removed" {
+ local name
+
+ name="test_$RANDOM"
+
+ wmutex_lock "$name"
+ wmutex_unlock "$name"
+ ! [ -e "$name" ]
+}
+
+@test "wmutex_unlock() returns failure if the lock was not removed" {
+ local name
+
+ # Unlikely to exist path
+ name="/$RANDOM/$RANDOM/$RANDOM/$RANDOM"
+
+ ! [ -d "${name%/*}" ]
+ ! wmutex_unlock "$name"
+}
+
+@test "wmutex_unlock() returns success if wmutex belongs to a different process" {
+ local name
+
+ name="test_$RANDOM"
+
+ wmutex_lock "$name"
+ delete+=("$name")
+
+ [ -L "$name" ]
+ ( wmutex_unlock "$name" )
+ ! [ -L "$name" ]
+}