"""
Reader-Writer lock implementation for thread-safe concurrent access.
Allows multiple concurrent readers or one exclusive writer.
"""
import threading


class RWLock:
    """
    Reader-Writer lock.

    - Multiple threads can acquire read lock simultaneously
    - Only one thread can acquire write lock (exclusive)
    - Read lock blocks if a write lock is active
    - Write lock blocks if any lock is active
    """

    def __init__(self):
        self._readers = 0
        self._writers = 0
        self._state_lock = threading.Condition(threading.Lock())   # Protects _readers and _writers
        self._writer_gate = threading.Condition(threading.Lock())  # Used to block writers

    def read_acquire(self):
        """Acquire read lock (shared)."""
        self._state_lock.acquire()
        try:
            while self._writers > 0:
                self._state_lock.wait()
            self._readers += 1
        finally:
            self._state_lock.release()

    def read_release(self):
        """Release read lock."""
        self._state_lock.acquire()
        try:
            self._readers -= 1
            if self._readers == 0:
                self._state_lock.notify_all()
        finally:
            self._state_lock.release()

    def write_acquire(self):
        """Acquire write lock (exclusive)."""
        self._state_lock.acquire()
        self._writers += 1
        self._state_lock.release()

        self._writer_gate .acquire()
        try:
            self._state_lock.acquire()
            try:
                while self._readers > 0:
                    self._state_lock.wait()
            finally:
                self._state_lock.release()
        except Exception:
            self._state_lock.acquire()
            self._writers -= 1
            self._state_lock.notify_all()
            self._state_lock.release()
            raise

    def write_release(self):
        """Release write lock."""
        self._writer_gate .release()
        self._state_lock.acquire()
        try:
            self._writers -= 1
            self._state_lock.notify_all()
        finally:
            self._state_lock.release()

    def read(self):
        """Context manager for read lock."""
        return _RWLockReadContext(self)

    def write(self):
        """Context manager for write lock."""
        return _RWLockWriteContext(self)


class _RWLockReadContext:
    """Context manager for read lock."""

    def __init__(self, lock):
        self.lock = lock

    def __enter__(self):
        self.lock.read_acquire()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.lock.read_release()
        return False


class _RWLockWriteContext:
    """Context manager for write lock."""

    def __init__(self, lock):
        self.lock = lock

    def __enter__(self):
        self.lock.write_acquire()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.lock.write_release()
        return False
