synch.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. """
  2. Synchronization primitives:
  3. - reader-writer lock (preference to writers)
  4. (Contributed to Django by eugene@lazutkin.com)
  5. """
  6. import contextlib
  7. try:
  8. import threading
  9. except ImportError:
  10. import dummy_threading as threading
  11. class RWLock(object):
  12. """
  13. Classic implementation of reader-writer lock with preference to writers.
  14. Readers can access a resource simultaneously.
  15. Writers get an exclusive access.
  16. API is self-descriptive:
  17. reader_enters()
  18. reader_leaves()
  19. writer_enters()
  20. writer_leaves()
  21. """
  22. def __init__(self):
  23. self.mutex = threading.RLock()
  24. self.can_read = threading.Semaphore(0)
  25. self.can_write = threading.Semaphore(0)
  26. self.active_readers = 0
  27. self.active_writers = 0
  28. self.waiting_readers = 0
  29. self.waiting_writers = 0
  30. def reader_enters(self):
  31. with self.mutex:
  32. if self.active_writers == 0 and self.waiting_writers == 0:
  33. self.active_readers += 1
  34. self.can_read.release()
  35. else:
  36. self.waiting_readers += 1
  37. self.can_read.acquire()
  38. def reader_leaves(self):
  39. with self.mutex:
  40. self.active_readers -= 1
  41. if self.active_readers == 0 and self.waiting_writers != 0:
  42. self.active_writers += 1
  43. self.waiting_writers -= 1
  44. self.can_write.release()
  45. @contextlib.contextmanager
  46. def reader(self):
  47. self.reader_enters()
  48. try:
  49. yield
  50. finally:
  51. self.reader_leaves()
  52. def writer_enters(self):
  53. with self.mutex:
  54. if self.active_writers == 0 and self.waiting_writers == 0 and self.active_readers == 0:
  55. self.active_writers += 1
  56. self.can_write.release()
  57. else:
  58. self.waiting_writers += 1
  59. self.can_write.acquire()
  60. def writer_leaves(self):
  61. with self.mutex:
  62. self.active_writers -= 1
  63. if self.waiting_writers != 0:
  64. self.active_writers += 1
  65. self.waiting_writers -= 1
  66. self.can_write.release()
  67. elif self.waiting_readers != 0:
  68. t = self.waiting_readers
  69. self.waiting_readers = 0
  70. self.active_readers += t
  71. while t > 0:
  72. self.can_read.release()
  73. t -= 1
  74. @contextlib.contextmanager
  75. def writer(self):
  76. self.writer_enters()
  77. try:
  78. yield
  79. finally:
  80. self.writer_leaves()