Skip to main content

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.

CI PyPI version Python Support License: MIT

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

  1. Lock Acquisition: Process attempts to bind to a TCP socket on localhost
  2. Port Discovery: Dynamic port allocation (OS-assigned) is written to a lock file
  3. Verification: Other processes can verify lock validity through handshake protocol
  4. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

socketlock-0.1.0.tar.gz (14.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

socketlock-0.1.0-py3-none-any.whl (9.8 kB view details)

Uploaded Python 3

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

Hashes for socketlock-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fdce0357ad3d77429fdec370fd79f4b5143a51a2bd73ab4284fc367109d6dc04
MD5 d662be42d894b33e47f7d2b23e2137a9
BLAKE2b-256 b67b7ec05272214811be8213b85bfd19ce39f3165e38a80c62f2b6c0488fd976

See more details on using hashes here.

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

Hashes for socketlock-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f4be8fac2d474577fdd7895cc984f3fd9bcc0ef6bef1a188ced86c85ec2f95da
MD5 a59e261b98b7dc16a0839dd1cfb0d736
BLAKE2b-256 08f18e0ccc74f96447629024f64b5c1ce90abf167886a95f7f8ee27ebe66bbfe

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page