|
35 | 35 |
|
36 | 36 |
|
37 | 37 | class ReadWriteLock: |
| 38 | + """ |
| 39 | + A read-write lock that allows multiple concurrent readers |
| 40 | + or one exclusive writer, implemented using Trio primitives. |
| 41 | + """ |
| 42 | + |
38 | 43 | def __init__(self) -> None: |
39 | 44 | self._readers = 0 |
40 | | - self._readers_lock = trio.Lock() # Protects readers count |
41 | | - self._writer_lock = trio.Semaphore(1) # Ensures mutual exclusion for writers |
| 45 | + self._readers_lock = trio.Lock() # Protects access to _readers count |
| 46 | + self._writer_lock = trio.Semaphore(1) # Allows only one writer at a time |
42 | 47 |
|
43 | 48 | async def acquire_read(self) -> None: |
| 49 | + """Acquire a read lock. Multiple readers can hold it simultaneously.""" |
44 | 50 | try: |
45 | 51 | async with self._readers_lock: |
46 | | - self._readers += 1 |
47 | | - if self._readers == 1: |
| 52 | + if self._readers == 0: |
48 | 53 | await self._writer_lock.acquire() |
| 54 | + self._readers += 1 |
49 | 55 | except trio.Cancelled: |
50 | | - async with self._readers_lock: |
51 | | - if self._readers > 0: |
52 | | - self._readers -= 1 |
53 | | - if self._readers == 0: |
54 | | - self._writer_lock.release() |
55 | 56 | raise |
56 | 57 |
|
57 | 58 | async def release_read(self) -> None: |
| 59 | + """Release a read lock.""" |
58 | 60 | async with self._readers_lock: |
59 | | - self._readers -= 1 |
60 | | - if self._readers == 0: |
| 61 | + if self._readers == 1: |
61 | 62 | self._writer_lock.release() |
| 63 | + self._readers -= 1 |
62 | 64 |
|
63 | 65 | async def acquire_write(self) -> None: |
| 66 | + """Acquire an exclusive write lock.""" |
64 | 67 | try: |
65 | 68 | await self._writer_lock.acquire() |
66 | 69 | except trio.Cancelled: |
67 | 70 | raise |
68 | 71 |
|
69 | 72 | def release_write(self) -> None: |
| 73 | + """Release the exclusive write lock.""" |
70 | 74 | self._writer_lock.release() |
71 | 75 |
|
72 | 76 | @asynccontextmanager |
73 | 77 | async def read_lock(self) -> AsyncGenerator[None, None]: |
74 | | - await self.acquire_read() |
| 78 | + """Context manager for acquiring and releasing a read lock safely.""" |
| 79 | + acquire = False |
75 | 80 | try: |
| 81 | + await self.acquire_read() |
| 82 | + acquire = True |
76 | 83 | yield |
77 | 84 | finally: |
78 | | - await self.release_read() |
| 85 | + if acquire: |
| 86 | + with trio.CancelScope() as scope: |
| 87 | + scope.shield = True |
| 88 | + await self.release_read() |
79 | 89 |
|
80 | 90 | @asynccontextmanager |
81 | 91 | async def write_lock(self) -> AsyncGenerator[None, None]: |
82 | | - await self.acquire_write() |
| 92 | + """Context manager for acquiring and releasing a write lock safely.""" |
| 93 | + acquire = False |
83 | 94 | try: |
| 95 | + await self.acquire_write() |
| 96 | + acquire = True |
84 | 97 | yield |
85 | 98 | finally: |
86 | | - self.release_write() |
| 99 | + if acquire: |
| 100 | + self.release_write() |
87 | 101 |
|
88 | 102 |
|
89 | 103 | class MplexStream(IMuxedStream): |
|
0 commit comments