| /* |
| Copyright 2005-2010 Intel Corporation. All Rights Reserved. |
| |
| This file is part of Threading Building Blocks. |
| |
| Threading Building Blocks is free software; you can redistribute it |
| and/or modify it under the terms of the GNU General Public License |
| version 2 as published by the Free Software Foundation. |
| |
| Threading Building Blocks is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with Threading Building Blocks; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| |
| As a special exception, you may use this file as part of a free software |
| library without restriction. Specifically, if other files instantiate |
| templates or use macros or inline functions from this file, or you compile |
| this file and link it with other files to produce an executable, this |
| file does not by itself cause the resulting executable to be covered by |
| the GNU General Public License. This exception does not however |
| invalidate any other reasons why the executable file might be covered by |
| the GNU General Public License. |
| */ |
| |
| #ifndef __TBB_reader_writer_lock_H |
| #define __TBB_reader_writer_lock_H |
| |
| #include "tbb_thread.h" |
| #include "tbb_allocator.h" |
| #include "atomic.h" |
| |
| namespace tbb { |
| namespace interface5 { |
| //! Writer-preference reader-writer lock with local-only spinning on readers. |
| /** Loosely adapted from Mellor-Crummey and Scott pseudocode at |
| http://www.cs.rochester.edu/research/synchronization/pseudocode/rw.html#s_wp |
| @ingroup synchronization */ |
| class reader_writer_lock : tbb::internal::no_copy { |
| public: |
| friend class scoped_lock; |
| friend class scoped_lock_read; |
| //! Status type for nodes associated with lock instances |
| /** waiting_nonblocking: the wait state for nonblocking lock |
| instances; for writes, these transition straight to active |
| states; for reads, these are unused. |
| |
| waiting: the start and spin state for all lock instances; these will |
| transition to active state when appropriate. Non-blocking write locks |
| transition from this state to waiting_nonblocking immediately. |
| |
| active: the active state means that the lock instance holds |
| the lock; it will transition to invalid state during node deletion |
| |
| invalid: the end state for all nodes; this is set in the |
| destructor so if we encounter this state, we are looking at |
| memory that has already been freed |
| |
| The state diagrams below describe the status transitions. |
| Single arrows indicate that the thread that owns the node is |
| responsible for the transition; double arrows indicate that |
| any thread could make the transition. |
| |
| State diagram for scoped_lock status: |
| |
| waiting ----------> waiting_nonblocking |
| | _____________/ | |
| V V V |
| active -----------------> invalid |
| |
| State diagram for scoped_lock_read status: |
| |
| waiting |
| | |
| V |
| active ----------------->invalid |
| |
| */ |
| enum status_t { waiting_nonblocking, waiting, active, invalid }; |
| |
| //! Constructs a new reader_writer_lock |
| reader_writer_lock() { |
| internal_construct(); |
| } |
| |
| //! Destructs a reader_writer_lock object |
| ~reader_writer_lock() { |
| internal_destroy(); |
| } |
| |
| //! The scoped lock pattern for write locks |
| /** Scoped locks help avoid the common problem of forgetting to release the lock. |
| This type is also serves as the node for queuing locks. */ |
| class scoped_lock : tbb::internal::no_copy { |
| public: |
| friend class reader_writer_lock; |
| |
| //! Construct with blocking attempt to acquire write lock on the passed-in lock |
| scoped_lock(reader_writer_lock& lock) { |
| internal_construct(lock); |
| } |
| |
| //! Destructor, releases the write lock |
| ~scoped_lock() { |
| internal_destroy(); |
| } |
| |
| void* operator new(size_t s) { |
| return tbb::internal::allocate_via_handler_v3(s); |
| } |
| void operator delete(void* p) { |
| tbb::internal::deallocate_via_handler_v3(p); |
| } |
| |
| private: |
| //! The pointer to the mutex to lock |
| reader_writer_lock *mutex; |
| //! The next queued competitor for the mutex |
| scoped_lock* next; |
| //! Status flag of the thread associated with this node |
| atomic<status_t> status; |
| |
| //! Construct scoped_lock that is not holding lock |
| scoped_lock(); |
| |
| void __TBB_EXPORTED_METHOD internal_construct(reader_writer_lock&); |
| void __TBB_EXPORTED_METHOD internal_destroy(); |
| }; |
| |
| //! The scoped lock pattern for read locks |
| class scoped_lock_read : tbb::internal::no_copy { |
| public: |
| friend class reader_writer_lock; |
| |
| //! Construct with blocking attempt to acquire read lock on the passed-in lock |
| scoped_lock_read(reader_writer_lock& lock) { |
| internal_construct(lock); |
| } |
| |
| //! Destructor, releases the read lock |
| ~scoped_lock_read() { |
| internal_destroy(); |
| } |
| |
| void* operator new(size_t s) { |
| return tbb::internal::allocate_via_handler_v3(s); |
| } |
| void operator delete(void* p) { |
| tbb::internal::deallocate_via_handler_v3(p); |
| } |
| |
| private: |
| //! The pointer to the mutex to lock |
| reader_writer_lock *mutex; |
| //! The next queued competitor for the mutex |
| scoped_lock_read *next; |
| //! Status flag of the thread associated with this node |
| atomic<status_t> status; |
| |
| //! Construct scoped_lock_read that is not holding lock |
| scoped_lock_read(); |
| |
| void __TBB_EXPORTED_METHOD internal_construct(reader_writer_lock&); |
| void __TBB_EXPORTED_METHOD internal_destroy(); |
| }; |
| |
| //! Acquires the reader_writer_lock for write. |
| /** If the lock is currently held in write mode by another |
| context, the writer will block by spinning on a local |
| variable. Exceptions thrown: improper_lock The context tries |
| to acquire a reader_writer_lock that it already has write |
| ownership of.*/ |
| void __TBB_EXPORTED_METHOD lock(); |
| |
| //! Tries to acquire the reader_writer_lock for write. |
| /** This function does not block. Return Value: True or false, |
| depending on whether the lock is acquired or not. If the lock |
| is already held by this acquiring context, try_lock() returns |
| false. */ |
| bool __TBB_EXPORTED_METHOD try_lock(); |
| |
| //! Acquires the reader_writer_lock for read. |
| /** If the lock is currently held by a writer, this reader will |
| block and wait until the writers are done. Exceptions thrown: |
| improper_lock The context tries to acquire a |
| reader_writer_lock that it already has write ownership of. */ |
| void __TBB_EXPORTED_METHOD lock_read(); |
| |
| //! Tries to acquire the reader_writer_lock for read. |
| /** This function does not block. Return Value: True or false, |
| depending on whether the lock is acquired or not. */ |
| bool __TBB_EXPORTED_METHOD try_lock_read(); |
| |
| //! Releases the reader_writer_lock |
| void __TBB_EXPORTED_METHOD unlock(); |
| |
| private: |
| void __TBB_EXPORTED_METHOD internal_construct(); |
| void __TBB_EXPORTED_METHOD internal_destroy(); |
| |
| //! Attempts to acquire write lock |
| /** If unavailable, spins in blocking case, returns false in non-blocking case. */ |
| bool start_write(scoped_lock *); |
| //! Sets writer_head to w and attempts to unblock |
| void set_next_writer(scoped_lock *w); |
| //! Relinquishes write lock to next waiting writer or group of readers |
| void end_write(scoped_lock *); |
| //! Checks if current thread holds write lock |
| bool is_current_writer(); |
| |
| //! Attempts to acquire read lock |
| /** If unavailable, spins in blocking case, returns false in non-blocking case. */ |
| void start_read(scoped_lock_read *); |
| //! Unblocks pending readers |
| void unblock_readers(); |
| //! Relinquishes read lock by decrementing counter; last reader wakes pending writer |
| void end_read(); |
| |
| //! The list of pending readers |
| atomic<scoped_lock_read*> reader_head; |
| //! The list of pending writers |
| atomic<scoped_lock*> writer_head; |
| //! The last node in the list of pending writers |
| atomic<scoped_lock*> writer_tail; |
| //! Writer that owns the mutex; tbb_thread::id() otherwise. |
| tbb_thread::id my_current_writer; |
| //! Status of mutex |
| atomic<unsigned> rdr_count_and_flags; |
| }; |
| |
| } // namespace interface5 |
| |
| using interface5::reader_writer_lock; |
| |
| } // namespace tbb |
| |
| #endif /* __TBB_reader_writer_lock_H */ |