| """ |
| Synchronization primitives: |
| |
| - reader-writer lock (preference to writers) |
| |
| (Contributed to Django by [email protected]) |
| """ |
| |
| from __future__ import with_statement |
| |
| import contextlib |
| try: |
| import threading |
| except ImportError: |
| import dummy_threading as threading |
| |
| |
| class RWLock(object): |
| """ |
| Classic implementation of reader-writer lock with preference to writers. |
| |
| Readers can access a resource simultaneously. |
| Writers get an exclusive access. |
| |
| API is self-descriptive: |
| reader_enters() |
| reader_leaves() |
| writer_enters() |
| writer_leaves() |
| """ |
| def __init__(self): |
| self.mutex = threading.RLock() |
| self.can_read = threading.Semaphore(0) |
| self.can_write = threading.Semaphore(0) |
| self.active_readers = 0 |
| self.active_writers = 0 |
| self.waiting_readers = 0 |
| self.waiting_writers = 0 |
| |
| def reader_enters(self): |
| with self.mutex: |
| if self.active_writers == 0 and self.waiting_writers == 0: |
| self.active_readers += 1 |
| self.can_read.release() |
| else: |
| self.waiting_readers += 1 |
| self.can_read.acquire() |
| |
| def reader_leaves(self): |
| with self.mutex: |
| self.active_readers -= 1 |
| if self.active_readers == 0 and self.waiting_writers != 0: |
| self.active_writers += 1 |
| self.waiting_writers -= 1 |
| self.can_write.release() |
| |
| @contextlib.contextmanager |
| def reader(self): |
| self.reader_enters() |
| try: |
| yield |
| finally: |
| self.reader_leaves() |
| |
| def writer_enters(self): |
| with self.mutex: |
| if self.active_writers == 0 and self.waiting_writers == 0 and self.active_readers == 0: |
| self.active_writers += 1 |
| self.can_write.release() |
| else: |
| self.waiting_writers += 1 |
| self.can_write.acquire() |
| |
| def writer_leaves(self): |
| with self.mutex: |
| self.active_writers -= 1 |
| if self.waiting_writers != 0: |
| self.active_writers += 1 |
| self.waiting_writers -= 1 |
| self.can_write.release() |
| elif self.waiting_readers != 0: |
| t = self.waiting_readers |
| self.waiting_readers = 0 |
| self.active_readers += t |
| while t > 0: |
| self.can_read.release() |
| t -= 1 |
| |
| @contextlib.contextmanager |
| def writer(self): |
| self.writer_enters() |
| try: |
| yield |
| finally: |
| self.writer_leaves() |