test_rw_lock.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. # Copyright Mateusz Kobos, (c) 2011
  2. # https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/
  3. # released under the MIT licence
  4. try:
  5. import unittest2 as unittest
  6. except ImportError:
  7. import unittest
  8. import threading
  9. import time
  10. import copy
  11. from ._rwlock import RWLock
  12. class Writer(threading.Thread):
  13. def __init__(
  14. self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write
  15. ):
  16. """
  17. @param buffer_: common buffer_ shared by the readers and writers
  18. @type buffer_: list
  19. @type rw_lock: L{RWLock}
  20. @param init_sleep_time: sleep time before doing any action
  21. @type init_sleep_time: C{float}
  22. @param sleep_time: sleep time while in critical section
  23. @type sleep_time: C{float}
  24. @param to_write: data that will be appended to the buffer
  25. """
  26. threading.Thread.__init__(self)
  27. self.__buffer = buffer_
  28. self.__rw_lock = rw_lock
  29. self.__init_sleep_time = init_sleep_time
  30. self.__sleep_time = sleep_time
  31. self.__to_write = to_write
  32. self.entry_time = None
  33. """Time of entry to the critical section"""
  34. self.exit_time = None
  35. """Time of exit from the critical section"""
  36. def run(self):
  37. time.sleep(self.__init_sleep_time)
  38. self.__rw_lock.writer_acquire()
  39. self.entry_time = time.time()
  40. time.sleep(self.__sleep_time)
  41. self.__buffer.append(self.__to_write)
  42. self.exit_time = time.time()
  43. self.__rw_lock.writer_release()
  44. class Reader(threading.Thread):
  45. def __init__(self, buffer_, rw_lock, init_sleep_time, sleep_time):
  46. """
  47. @param buffer_: common buffer shared by the readers and writers
  48. @type buffer_: list
  49. @type rw_lock: L{RWLock}
  50. @param init_sleep_time: sleep time before doing any action
  51. @type init_sleep_time: C{float}
  52. @param sleep_time: sleep time while in critical section
  53. @type sleep_time: C{float}
  54. """
  55. threading.Thread.__init__(self)
  56. self.__buffer = buffer_
  57. self.__rw_lock = rw_lock
  58. self.__init_sleep_time = init_sleep_time
  59. self.__sleep_time = sleep_time
  60. self.buffer_read = None
  61. """a copy of a the buffer read while in critical section"""
  62. self.entry_time = None
  63. """Time of entry to the critical section"""
  64. self.exit_time = None
  65. """Time of exit from the critical section"""
  66. def run(self):
  67. time.sleep(self.__init_sleep_time)
  68. self.__rw_lock.reader_acquire()
  69. self.entry_time = time.time()
  70. time.sleep(self.__sleep_time)
  71. self.buffer_read = copy.deepcopy(self.__buffer)
  72. self.exit_time = time.time()
  73. self.__rw_lock.reader_release()
  74. class RWLockTestCase(unittest.TestCase):
  75. def test_readers_nonexclusive_access(self):
  76. (buffer_, rw_lock, threads) = self.__init_variables()
  77. threads.append(Reader(buffer_, rw_lock, 0, 0))
  78. threads.append(Writer(buffer_, rw_lock, 0.2, 0.4, 1))
  79. threads.append(Reader(buffer_, rw_lock, 0.3, 0.3))
  80. threads.append(Reader(buffer_, rw_lock, 0.5, 0))
  81. self.__start_and_join_threads(threads)
  82. ## The third reader should enter after the second one but it should
  83. ## exit before the second one exits
  84. ## (i.e. the readers should be in the critical section
  85. ## at the same time)
  86. self.assertEqual([], threads[0].buffer_read)
  87. self.assertEqual([1], threads[2].buffer_read)
  88. self.assertEqual([1], threads[3].buffer_read)
  89. self.assertTrue(threads[1].exit_time <= threads[2].entry_time)
  90. self.assertTrue(threads[2].entry_time <= threads[3].entry_time)
  91. self.assertTrue(threads[3].exit_time < threads[2].exit_time)
  92. def test_writers_exclusive_access(self):
  93. (buffer_, rw_lock, threads) = self.__init_variables()
  94. threads.append(Writer(buffer_, rw_lock, 0, 0.4, 1))
  95. threads.append(Writer(buffer_, rw_lock, 0.1, 0, 2))
  96. threads.append(Reader(buffer_, rw_lock, 0.2, 0))
  97. self.__start_and_join_threads(threads)
  98. ## The second writer should wait for the first one to exit
  99. self.assertEqual([1, 2], threads[2].buffer_read)
  100. self.assertTrue(threads[0].exit_time <= threads[1].entry_time)
  101. self.assertTrue(threads[1].exit_time <= threads[2].exit_time)
  102. def test_writer_priority(self):
  103. (buffer_, rw_lock, threads) = self.__init_variables()
  104. threads.append(Writer(buffer_, rw_lock, 0, 0, 1))
  105. threads.append(Reader(buffer_, rw_lock, 0.1, 0.4))
  106. threads.append(Writer(buffer_, rw_lock, 0.2, 0, 2))
  107. threads.append(Reader(buffer_, rw_lock, 0.3, 0))
  108. threads.append(Reader(buffer_, rw_lock, 0.3, 0))
  109. self.__start_and_join_threads(threads)
  110. ## The second writer should go before the second and the third reader
  111. self.assertEqual([1], threads[1].buffer_read)
  112. self.assertEqual([1, 2], threads[3].buffer_read)
  113. self.assertEqual([1, 2], threads[4].buffer_read)
  114. self.assertTrue(threads[0].exit_time < threads[1].entry_time)
  115. self.assertTrue(threads[1].exit_time <= threads[2].entry_time)
  116. self.assertTrue(threads[2].exit_time <= threads[3].entry_time)
  117. self.assertTrue(threads[2].exit_time <= threads[4].entry_time)
  118. def test_many_writers_priority(self):
  119. (buffer_, rw_lock, threads) = self.__init_variables()
  120. threads.append(Writer(buffer_, rw_lock, 0, 0, 1))
  121. threads.append(Reader(buffer_, rw_lock, 0.1, 0.6))
  122. threads.append(Writer(buffer_, rw_lock, 0.2, 0.1, 2))
  123. threads.append(Reader(buffer_, rw_lock, 0.3, 0))
  124. threads.append(Reader(buffer_, rw_lock, 0.4, 0))
  125. threads.append(Writer(buffer_, rw_lock, 0.5, 0.1, 3))
  126. self.__start_and_join_threads(threads)
  127. ## The two last writers should go first -- after the first reader and
  128. ## before the second and the third reader
  129. self.assertEqual([1], threads[1].buffer_read)
  130. self.assertEqual([1, 2, 3], threads[3].buffer_read)
  131. self.assertEqual([1, 2, 3], threads[4].buffer_read)
  132. self.assertTrue(threads[0].exit_time < threads[1].entry_time)
  133. self.assertTrue(threads[1].exit_time <= threads[2].entry_time)
  134. self.assertTrue(threads[1].exit_time <= threads[5].entry_time)
  135. self.assertTrue(threads[2].exit_time <= threads[3].entry_time)
  136. self.assertTrue(threads[2].exit_time <= threads[4].entry_time)
  137. self.assertTrue(threads[5].exit_time <= threads[3].entry_time)
  138. self.assertTrue(threads[5].exit_time <= threads[4].entry_time)
  139. @staticmethod
  140. def __init_variables():
  141. buffer_ = []
  142. rw_lock = RWLock()
  143. threads = []
  144. return (buffer_, rw_lock, threads)
  145. @staticmethod
  146. def __start_and_join_threads(threads):
  147. for t in threads:
  148. t.start()
  149. for t in threads:
  150. t.join()