123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /*
- Copyright 2005-2013 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 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<uintptr_t> rdr_count_and_flags; // used with __TBB_AtomicOR, which assumes uintptr_t
- };
- } // namespace interface5
- using interface5::reader_writer_lock;
- } // namespace tbb
- #endif /* __TBB_reader_writer_lock_H */
|