Skip to content

Add a no-op lock implementation #25

@pomponchik

Description

@pomponchik

It would be useful for locklib to provide a built-in lock implementation that satisfies the context-lock interface while deliberately doing no locking.

In throng, we currently keep a small local implementation for this purpose:

from types import TracebackType
from typing import Optional, Type


class EmptyLock:
    """
    Provide the context-lock interface while deliberately doing no locking.

    Temporary isolates receive this lock so they can reuse ``DirectoryIsolate``
    without acquiring the serialization policy of the local plugin.
    """

    def __enter__(self) -> None:
        """Enter a no-op critical section."""
        self.acquire()

    def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]) -> None:
        """Leave a no-op critical section without suppressing exceptions."""
        self.release()

    def acquire(self) -> None:
        """Accept a lock acquisition request without blocking."""

    def release(self) -> None:
        """Accept a matching lock release request without side effects."""

Why this is useful

Some code is written against a lock-like interface because the operation may need serialization in one backend, but not in another.

For example, throng has two filesystem-backed isolate modes:

  • a local isolate, where multiple operations share one directory and must be serialized;
  • a temporary isolate, where each isolate owns a fresh directory and does not need that serialization.

Both modes can reuse the same implementation if the lock is injected as a dependency. The local mode passes a real lock, while the temporary mode passes a no-op lock.

This keeps the main implementation simpler:

with self.lock:
    return self._run_unlocked(...)

The caller decides whether self.lock actually serializes anything.

Proposed API

A small class like this could live in locklib, for example:

from locklib import EmptyLock

Possible names:

  • EmptyLock
  • NoOpLock
  • NullLock

EmptyLock matches the existing naming in throng, but NoOpLock may be more immediately recognizable.

Expected behavior

The lock should:

  • implement the same context-manager shape as other locklib-compatible locks;
  • expose acquire() and release();
  • never block;
  • never suppress exceptions from the wrapped block;
  • have no side effects;
  • be reusable across repeated with lock: blocks.

Minimal behavior example:

lock = EmptyLock()

with lock:
    pass

lock.acquire()
lock.release()

Benefit

Providing this in locklib would avoid each downstream project having to define its own local no-op lock, while still encouraging dependency injection of lock behavior instead of branching inside business logic.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions