232 lines
8.8 KiB
C++
232 lines
8.8 KiB
C++
/*
|
|
Copyright 2005-2016 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 */
|