You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

91 lines
2.5 KiB

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