Stop Shell-Script Collisions with WaitLock: A Friendly Guide for Everyone
“
When two scripts try to back up the same database, download the same file, or occupy the same GPU at the same time, something usually breaks.
WaitLock is a tiny, portable command-line tool that gives your shell scripts traffic lights—mutexes and semaphores that work on any Unix-like system.
Below you will find every detail you need: how to install it, how to use it, and how to adapt it to real production work. Nothing has been added from outside sources; everything comes straight from the official project documentation.
Why WaitLock Exists
Traditional locking tools (flock
, lockfile
) often leave stale locks behind when a process crashes.
WaitLock was written to fix three pain points:
-
Automatic cleanup – locks vanish as soon as the process ends, no matter how it ends. -
Single-command usage – one line to acquire a lock and run a script. -
Cross-platform portability – pure C, tested on Linux, FreeBSD, OpenBSD, NetBSD, and macOS.
It supports two modes:
-
Mutex (default) – one owner at a time. -
Semaphore – up to N owners in parallel.
Quick Installation in Three Moves
1. Install build tools
Ubuntu / Debian
sudo apt-get update
sudo apt-get install build-essential autoconf
CentOS / Rocky
sudo yum groupinstall "Development Tools"
sudo yum install autoconf
macOS
xcode-select --install # command-line tools
brew install autoconf # if you use Homebrew
2. Build from source
git clone https://github.com/user/waitlock.git
cd waitlock
autoreconf -fi # only needed when building from git
./configure
make
Optional build flavours
# Debug build
./configure CFLAGS="-g -O0 -DDEBUG"
# Release build
./configure CFLAGS="-O2 -DNDEBUG"
# Cross-compile for ARM
./configure --host=arm-linux-gnueabihf
3. Install system-wide or locally
sudo make install # into /usr/local/bin
# or
./configure --prefix=/opt/waitlock
make install
Check success:
waitlock --version # should print 1.0.0
One-Minute Primer
Exclusive daily backup
#!/bin/bash
# nightly_backup.sh
waitlock nightly_backup || { echo "Another backup is running"; exit 1; }
mysqldump --all-databases > all.sql
tar czf all.sql.tgz all.sql
Run it twice:
./nightly_backup.sh &
./nightly_backup.sh # second instance exits immediately
Allow four parallel downloads
#!/bin/bash
# download.sh
waitlock --allowMultiple 4 download_slot || { echo "Pool full"; exit 1; }
wget "$1"
Feed it 100 URLs:
cat urls.txt | xargs -n1 -P8 ./download.sh
At most four downloads run at once; the rest queue automatically.
Command Cheat-Sheet
Ten Real-World Recipes
1. Single-instance backup script
#!/bin/bash
waitlock db_backup --exec bash -c "
mysqldump --all-databases > backup.sql
gzip backup.sql
"
The lock disappears when the subshell exits.
2. GPU resource pool
Eight-GPU server, keep one GPU free for the OS:
waitlock -c -x 1 gpu_pool
export CUDA_VISIBLE_DEVICES=$WAITLOCK_SLOT
python train.py
$WAITLOCK_SLOT
gives you an index between 0 and 6.
3. CPU-aware batch jobs
Run one job per core, minus two reserved cores:
waitlock -c -x 2 cpu_job --exec ./heavy_computation.sh
4. Parallel file processing with controlled fan-out
find /data -name "*.csv" | \
xargs -P10 -I{} waitlock -m 3 processor --exec "python process.py {}"
xargs
starts ten workers; WaitLock keeps only three active at any moment.
5. NFS cross-node lock
Shared storage at /mnt/nfs
:
export WAITLOCK_DIR="/mnt/nfs/locks"
waitlock cluster_job --timeout 300 --exec ./mpi_job.sh
Works across any POSIX machine mounting the same NFS share.
6. CI/CD pipeline gate
GitLab example:
deploy:
script:
- waitlock prod_deploy --timeout 600 --exec ./deploy.sh
Only one pipeline deploys at a time; others wait or fail after ten minutes.
7. Lock inspection during incidents
List everything in human-readable form:
waitlock --list
CSV for scripts:
waitlock --list --format csv | tail -n +2 | wc -l # count active locks
8. Environment-driven defaults
Create /etc/profile.d/waitlock.sh
:
export WAITLOCK_TIMEOUT=300
export WAITLOCK_DIR="/var/lock/myapp"
export WAITLOCK_DEBUG=1
Every script inherits these values; no need to repeat -t 300
.
9. Error handling in shell scripts
waitlock --timeout 30 critical_section
case $? in
0) echo "Lock acquired" ;;
1) echo "Already busy" >&2; exit 1 ;;
2) echo "Timeout" >&2; exit 1 ;;
*) echo "Unknown error" >&2; exit 1 ;;
esac
10. Syslog integration for audits
waitlock --syslog --syslog-facility local0 backup_job
Then watch:
tail -f /var/log/syslog | grep waitlock
Advanced Configuration
Environment variables reference
Example session:
WAITLOCK_TIMEOUT=60 \
WAITLOCK_DIR=$HOME/locks \
waitlock -m 4 gpu_pool
Exit codes
Troubleshooting Quick-Start
Performance Tips
-
Place lock directory on tmpfs
sudo mount -t tmpfs -o size=10M tmpfs /var/lock/waitlock
-
Avoid flat namespaces
project/service/task
reduces directory scan time. -
Tune timeout values
Balance between fast failure and queue pressure.
Under the Hood (No Secrets, Just Facts)
-
Lock file format: binary header with magic 0x57414C4B
, PID/UID metadata, CRC32 checksum. -
Cleanup mechanism: fcntl
advisory locks plusatexit
handlers; if the kernel revokes the lock, the next opener detects the stale file and removes it. -
Portability layer: POSIX fcntl
,sysconf(_SC_NPROCESSORS_ONLN)
, and classicopen(O_CREAT|O_EXCL)
—no Linux-only syscalls.
Contributing (If You Want To)
Development quick start
git clone https://github.com/user/waitlock.git
cd waitlock
sudo apt-get install autoconf automake libtool # or the macOS equivalents
autoreconf -fi
./configure --enable-debug CFLAGS="-g -O0"
make && make check
Test suites
./src/waitlock --test # built-in unit tests
./test_basic.sh
./test_semaphore.sh
./test_timeout.sh
Code style
-
Stick to C89/C90 -
4-space indentation -
Every new feature needs a test
License and Support
WaitLock is released under the MIT License.
For questions or bug reports:
-
Documentation: man waitlock
after installation -
Issues: https://github.com/user/waitlock/issues -
Discussions: https://github.com/user/waitlock/discussions
Closing Thoughts
Adding WaitLock to your toolkit is like installing traffic lights where previously there were none.
Backups no longer collide, GPUs are shared fairly, and your CI pipelines stop stepping on each other.
Install it once, use it everywhere, and forget about lock-file debris forever.
Happy scripting—and may your processes always run in the right lane.