A robust, secure, and async-friendly process lock using TCP sockets with handshake protocol
Project description
socketlock
A robust, secure, and async-friendly process lock using TCP sockets with handshake protocol verification.
Features
- 🔒 Atomic Lock Acquisition: Prevents race conditions with atomic file operations
- 🛡️ Security Handshake: Application-level protocol prevents imposter processes
- ⚡ Async-First: Built on asyncio with synchronous wrapper available
- 🔄 Self-Healing: Automatic detection and cleanup of stale locks
- 📊 Rich Diagnostics: Lock info includes PID and port for easy troubleshooting
- 🚀 High Performance: Sub-millisecond lock operations
- 💥 Crash-Safe: OS automatically releases sockets on process termination
- 🌍 Cross-Platform: Works on Linux, macOS, and Windows
Why socketlock?
Traditional process locks suffer from various problems:
- PID files: Process IDs can be recycled, causing false positives
- File locks: Often platform-specific, may not release on crash
- Semaphores: Require cleanup, complex API, platform differences
socketlock solves these issues by using TCP sockets with a verification handshake, ensuring that only legitimate processes can hold locks and that locks are always released on process termination.
Installation
pip install socketlock
Quick Start
Async Interface
import asyncio
from socketlock import AsyncSocketLock
async def critical_section():
async with AsyncSocketLock(name="myapp") as lock:
print(f"Lock acquired (PID: {lock.pid}, Port: {lock.port})")
# Your protected code here
await asyncio.sleep(1)
# Lock automatically released
asyncio.run(critical_section())
Sync Interface
from socketlock import SocketLock
with SocketLock(name="myapp") as lock:
print(f"Lock acquired (PID: {lock.pid}, Port: {lock.port})")
# Your protected code here
# Lock automatically released
Manual Lock Management
import asyncio
from socketlock import AsyncSocketLock
async def manual_lock():
lock = AsyncSocketLock(name="myapp")
try:
await lock.acquire()
print("Lock acquired!")
# Your protected code here
except RuntimeError as e:
print(f"Could not acquire lock: {e}")
# Error includes PID and port of lock holder
finally:
await lock.release()
asyncio.run(manual_lock())
Advanced Usage
Custom Configuration
from socketlock import AsyncSocketLock
lock = AsyncSocketLock(
name="myapp",
timeout=7200, # Stale lock timeout (seconds)
lock_dir="/var/run/myapp", # Custom lock directory
signature_seed="custom_seed", # Custom handshake seed
)
Checking Lock Status
import asyncio
from socketlock import AsyncSocketLock
async def check_status():
lock = AsyncSocketLock(name="myapp")
info = await lock.get_lock_info()
if info:
print(f"Lock is held by PID {info['pid']} on port {info['port']}")
print(f"Lock acquired at: {info['timestamp']}")
else:
print("Lock is available")
asyncio.run(check_status())
Non-Blocking Acquisition
import asyncio
from socketlock import AsyncSocketLock
async def try_lock():
lock = AsyncSocketLock(name="myapp")
if await lock.try_acquire():
print("Got the lock!")
# Do work...
await lock.release()
else:
print("Lock is busy, will try again later")
asyncio.run(try_lock())
How It Works
- Lock Acquisition: Process attempts to bind to a TCP socket on localhost
- Port Discovery: Dynamic port allocation (OS-assigned) is written to a lock file
- Verification: Other processes can verify lock validity through handshake protocol
- Automatic Release: OS releases socket when process terminates (any reason)
The handshake protocol prevents denial-of-service attacks where a rogue process could bind to a port and pretend to hold the lock.
Performance
Typical operation times (Intel i7, Python 3.11):
- Uncontested acquisition: ~0.5ms
- Contested detection: ~0.7ms (including handshake verification)
- Lock release: ~0.06ms
- Full context manager cycle: ~0.6ms
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file socketlock-0.1.0.tar.gz.
File metadata
- Download URL: socketlock-0.1.0.tar.gz
- Upload date:
- Size: 14.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fdce0357ad3d77429fdec370fd79f4b5143a51a2bd73ab4284fc367109d6dc04
|
|
| MD5 |
d662be42d894b33e47f7d2b23e2137a9
|
|
| BLAKE2b-256 |
b67b7ec05272214811be8213b85bfd19ce39f3165e38a80c62f2b6c0488fd976
|
File details
Details for the file socketlock-0.1.0-py3-none-any.whl.
File metadata
- Download URL: socketlock-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4be8fac2d474577fdd7895cc984f3fd9bcc0ef6bef1a188ced86c85ec2f95da
|
|
| MD5 |
a59e261b98b7dc16a0839dd1cfb0d736
|
|
| BLAKE2b-256 |
08f18e0ccc74f96447629024f64b5c1ce90abf167886a95f7f8ee27ebe66bbfe
|