reader_writer_lock.h 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. Copyright 2005-2013 Intel Corporation. All Rights Reserved.
  3. This file is part of Threading Building Blocks.
  4. Threading Building Blocks is free software; you can redistribute it
  5. and/or modify it under the terms of the GNU General Public License
  6. version 2 as published by the Free Software Foundation.
  7. Threading Building Blocks is distributed in the hope that it will be
  8. useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  9. of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with Threading Building Blocks; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  14. As a special exception, you may use this file as part of a free software
  15. library without restriction. Specifically, if other files instantiate
  16. templates or use macros or inline functions from this file, or you compile
  17. this file and link it with other files to produce an executable, this
  18. file does not by itself cause the resulting executable to be covered by
  19. the GNU General Public License. This exception does not however
  20. invalidate any other reasons why the executable file might be covered by
  21. the GNU General Public License.
  22. */
  23. #ifndef __TBB_reader_writer_lock_H
  24. #define __TBB_reader_writer_lock_H
  25. #include "tbb_thread.h"
  26. #include "tbb_allocator.h"
  27. #include "atomic.h"
  28. namespace tbb {
  29. namespace interface5 {
  30. //! Writer-preference reader-writer lock with local-only spinning on readers.
  31. /** Loosely adapted from Mellor-Crummey and Scott pseudocode at
  32. http://www.cs.rochester.edu/research/synchronization/pseudocode/rw.html#s_wp
  33. @ingroup synchronization */
  34. class reader_writer_lock : tbb::internal::no_copy {
  35. public:
  36. friend class scoped_lock;
  37. friend class scoped_lock_read;
  38. //! Status type for nodes associated with lock instances
  39. /** waiting_nonblocking: the wait state for nonblocking lock
  40. instances; for writes, these transition straight to active
  41. states; for reads, these are unused.
  42. waiting: the start and spin state for all lock instances; these will
  43. transition to active state when appropriate. Non-blocking write locks
  44. transition from this state to waiting_nonblocking immediately.
  45. active: the active state means that the lock instance holds
  46. the lock; it will transition to invalid state during node deletion
  47. invalid: the end state for all nodes; this is set in the
  48. destructor so if we encounter this state, we are looking at
  49. memory that has already been freed
  50. The state diagrams below describe the status transitions.
  51. Single arrows indicate that the thread that owns the node is
  52. responsible for the transition; double arrows indicate that
  53. any thread could make the transition.
  54. State diagram for scoped_lock status:
  55. waiting ----------> waiting_nonblocking
  56. | _____________/ |
  57. V V V
  58. active -----------------> invalid
  59. State diagram for scoped_lock_read status:
  60. waiting
  61. |
  62. V
  63. active ----------------->invalid
  64. */
  65. enum status_t { waiting_nonblocking, waiting, active, invalid };
  66. //! Constructs a new reader_writer_lock
  67. reader_writer_lock() {
  68. internal_construct();
  69. }
  70. //! Destructs a reader_writer_lock object
  71. ~reader_writer_lock() {
  72. internal_destroy();
  73. }
  74. //! The scoped lock pattern for write locks
  75. /** Scoped locks help avoid the common problem of forgetting to release the lock.
  76. This type also serves as the node for queuing locks. */
  77. class scoped_lock : tbb::internal::no_copy {
  78. public:
  79. friend class reader_writer_lock;
  80. //! Construct with blocking attempt to acquire write lock on the passed-in lock
  81. scoped_lock(reader_writer_lock& lock) {
  82. internal_construct(lock);
  83. }
  84. //! Destructor, releases the write lock
  85. ~scoped_lock() {
  86. internal_destroy();
  87. }
  88. void* operator new(size_t s) {
  89. return tbb::internal::allocate_via_handler_v3(s);
  90. }
  91. void operator delete(void* p) {
  92. tbb::internal::deallocate_via_handler_v3(p);
  93. }
  94. private:
  95. //! The pointer to the mutex to lock
  96. reader_writer_lock *mutex;
  97. //! The next queued competitor for the mutex
  98. scoped_lock* next;
  99. //! Status flag of the thread associated with this node
  100. atomic<status_t> status;
  101. //! Construct scoped_lock that is not holding lock
  102. scoped_lock();
  103. void __TBB_EXPORTED_METHOD internal_construct(reader_writer_lock&);
  104. void __TBB_EXPORTED_METHOD internal_destroy();
  105. };
  106. //! The scoped lock pattern for read locks
  107. class scoped_lock_read : tbb::internal::no_copy {
  108. public:
  109. friend class reader_writer_lock;
  110. //! Construct with blocking attempt to acquire read lock on the passed-in lock
  111. scoped_lock_read(reader_writer_lock& lock) {
  112. internal_construct(lock);
  113. }
  114. //! Destructor, releases the read lock
  115. ~scoped_lock_read() {
  116. internal_destroy();
  117. }
  118. void* operator new(size_t s) {
  119. return tbb::internal::allocate_via_handler_v3(s);
  120. }
  121. void operator delete(void* p) {
  122. tbb::internal::deallocate_via_handler_v3(p);
  123. }
  124. private:
  125. //! The pointer to the mutex to lock
  126. reader_writer_lock *mutex;
  127. //! The next queued competitor for the mutex
  128. scoped_lock_read *next;
  129. //! Status flag of the thread associated with this node
  130. atomic<status_t> status;
  131. //! Construct scoped_lock_read that is not holding lock
  132. scoped_lock_read();
  133. void __TBB_EXPORTED_METHOD internal_construct(reader_writer_lock&);
  134. void __TBB_EXPORTED_METHOD internal_destroy();
  135. };
  136. //! Acquires the reader_writer_lock for write.
  137. /** If the lock is currently held in write mode by another
  138. context, the writer will block by spinning on a local
  139. variable. Exceptions thrown: improper_lock The context tries
  140. to acquire a reader_writer_lock that it already has write
  141. ownership of.*/
  142. void __TBB_EXPORTED_METHOD lock();
  143. //! Tries to acquire the reader_writer_lock for write.
  144. /** This function does not block. Return Value: True or false,
  145. depending on whether the lock is acquired or not. If the lock
  146. is already held by this acquiring context, try_lock() returns
  147. false. */
  148. bool __TBB_EXPORTED_METHOD try_lock();
  149. //! Acquires the reader_writer_lock for read.
  150. /** If the lock is currently held by a writer, this reader will
  151. block and wait until the writers are done. Exceptions thrown:
  152. improper_lock The context tries to acquire a
  153. reader_writer_lock that it already has write ownership of. */
  154. void __TBB_EXPORTED_METHOD lock_read();
  155. //! Tries to acquire the reader_writer_lock for read.
  156. /** This function does not block. Return Value: True or false,
  157. depending on whether the lock is acquired or not. */
  158. bool __TBB_EXPORTED_METHOD try_lock_read();
  159. //! Releases the reader_writer_lock
  160. void __TBB_EXPORTED_METHOD unlock();
  161. private:
  162. void __TBB_EXPORTED_METHOD internal_construct();
  163. void __TBB_EXPORTED_METHOD internal_destroy();
  164. //! Attempts to acquire write lock
  165. /** If unavailable, spins in blocking case, returns false in non-blocking case. */
  166. bool start_write(scoped_lock *);
  167. //! Sets writer_head to w and attempts to unblock
  168. void set_next_writer(scoped_lock *w);
  169. //! Relinquishes write lock to next waiting writer or group of readers
  170. void end_write(scoped_lock *);
  171. //! Checks if current thread holds write lock
  172. bool is_current_writer();
  173. //! Attempts to acquire read lock
  174. /** If unavailable, spins in blocking case, returns false in non-blocking case. */
  175. void start_read(scoped_lock_read *);
  176. //! Unblocks pending readers
  177. void unblock_readers();
  178. //! Relinquishes read lock by decrementing counter; last reader wakes pending writer
  179. void end_read();
  180. //! The list of pending readers
  181. atomic<scoped_lock_read*> reader_head;
  182. //! The list of pending writers
  183. atomic<scoped_lock*> writer_head;
  184. //! The last node in the list of pending writers
  185. atomic<scoped_lock*> writer_tail;
  186. //! Writer that owns the mutex; tbb_thread::id() otherwise.
  187. tbb_thread::id my_current_writer;
  188. //! Status of mutex
  189. atomic<uintptr_t> rdr_count_and_flags; // used with __TBB_AtomicOR, which assumes uintptr_t
  190. };
  191. } // namespace interface5
  192. using interface5::reader_writer_lock;
  193. } // namespace tbb
  194. #endif /* __TBB_reader_writer_lock_H */