2014-07-04 16:17:05 -04:00
|
|
|
//
|
|
|
|
|
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#ifndef MONITOR_HPP_
|
|
|
|
|
#define MONITOR_HPP_
|
|
|
|
|
|
|
|
|
|
#include "top.hpp"
|
2014-09-29 17:38:55 -04:00
|
|
|
#include "thread/atomic.hpp"
|
2014-07-04 16:17:05 -04:00
|
|
|
#include "thread/semaphore.hpp"
|
|
|
|
|
#include "thread/thread.hpp"
|
|
|
|
|
|
2014-09-29 17:38:55 -04:00
|
|
|
#include <atomic>
|
2014-07-04 16:17:05 -04:00
|
|
|
#include <tuple>
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
namespace amd {
|
|
|
|
|
|
|
|
|
|
/*! \addtogroup Threads
|
|
|
|
|
* @{
|
|
|
|
|
*
|
|
|
|
|
* \addtogroup Synchronization
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
2014-09-29 17:38:55 -04:00
|
|
|
namespace details {
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
template <class T, class AllocClass = HeapObject> struct SimplyLinkedNode : public AllocClass {
|
|
|
|
|
typedef SimplyLinkedNode<T, AllocClass> Node;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
std::atomic<Node*> next_; /*!< \brief The next element. */
|
|
|
|
|
T volatile item_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
//! \brief Return the next element in the linked-list.
|
|
|
|
|
Node* next() const { return next_; }
|
|
|
|
|
//! \brief Return the item.
|
|
|
|
|
T item() const { return item_; }
|
|
|
|
|
|
|
|
|
|
//! \brief Set the next element pointer.
|
|
|
|
|
void setNext(Node* next) { next_ = next; }
|
|
|
|
|
//! \brief Set the item.
|
|
|
|
|
void setItem(T item) { item_ = item; }
|
|
|
|
|
|
|
|
|
|
//! \brief Swap the next element pointer.
|
|
|
|
|
Node* swapNext(Node* next) { return next_.swap(next); }
|
|
|
|
|
|
|
|
|
|
//! \brief Compare and set the next element pointer.
|
|
|
|
|
bool compareAndSetNext(Node* compare, Node* next) {
|
|
|
|
|
return next_.compare_exchange_strong(compare, next);
|
|
|
|
|
}
|
2014-09-29 17:38:55 -04:00
|
|
|
};
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
} // namespace details
|
|
|
|
|
|
|
|
|
|
class Monitor : public HeapObject {
|
|
|
|
|
typedef details::SimplyLinkedNode<Semaphore*, StackObject> LinkedNode;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
static const intptr_t kLockBit = 0x1;
|
|
|
|
|
|
|
|
|
|
static const int kMaxSpinIter = 55; //!< Total number of spin iterations.
|
|
|
|
|
static const int kMaxReadSpinIter = 50; //!< Read iterations before yielding
|
|
|
|
|
|
|
|
|
|
/*! Linked list of semaphores the contending threads are waiting on
|
|
|
|
|
* and main lock.
|
|
|
|
|
*/
|
|
|
|
|
std::atomic_intptr_t contendersList_;
|
|
|
|
|
//! The Mutex's name
|
|
|
|
|
char name_[64];
|
|
|
|
|
|
|
|
|
|
//! Semaphore of the next thread to contend for the lock.
|
|
|
|
|
std::atomic_intptr_t onDeck_;
|
|
|
|
|
//! Linked list of the suspended threads resume semaphores.
|
|
|
|
|
LinkedNode* volatile waitersList_;
|
|
|
|
|
|
|
|
|
|
//! Thread owning this monitor.
|
|
|
|
|
Thread* volatile owner_;
|
|
|
|
|
//! The amount of times this monitor was acquired by the owner.
|
|
|
|
|
uint32_t lockCount_;
|
|
|
|
|
//! True if this is a recursive mutex, false otherwise.
|
|
|
|
|
const bool recursive_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
//! Finish locking the mutex (contented case).
|
|
|
|
|
void finishLock();
|
|
|
|
|
//! Finish unlocking the mutex (contented case).
|
|
|
|
|
void finishUnlock();
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
//! Try to spin-acquire the lock, return true if successful.
|
|
|
|
|
bool trySpinLock();
|
|
|
|
|
|
|
|
|
|
/*! \brief Return true if the lock is owned.
|
|
|
|
|
*
|
|
|
|
|
* \note The user is responsible for the memory ordering.
|
|
|
|
|
*/
|
|
|
|
|
bool isLocked() const { return (contendersList_ & kLockBit) != 0; }
|
|
|
|
|
|
|
|
|
|
//! Return this monitor's owner thread (NULL if unlocked).
|
|
|
|
|
Thread* owner() const { return owner_; }
|
|
|
|
|
|
|
|
|
|
//! Set the owner.
|
|
|
|
|
void setOwner(Thread* thread) { owner_ = thread; }
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
explicit Monitor(const char* name = NULL, bool recursive = false);
|
|
|
|
|
~Monitor() {}
|
|
|
|
|
|
|
|
|
|
//! Try to acquire the lock, return true if successful.
|
|
|
|
|
inline bool tryLock();
|
|
|
|
|
|
|
|
|
|
//! Acquire the lock or suspend the calling thread.
|
|
|
|
|
inline void lock();
|
|
|
|
|
|
|
|
|
|
//! Release the lock and wake a single waiting thread if any.
|
|
|
|
|
inline void unlock();
|
|
|
|
|
|
|
|
|
|
/*! \brief Give up the lock and go to sleep.
|
|
|
|
|
*
|
|
|
|
|
* Calling wait() causes the current thread to go to sleep until
|
|
|
|
|
* another thread calls notify()/notifyAll().
|
|
|
|
|
*
|
|
|
|
|
* \note The monitor must be owned before calling wait().
|
|
|
|
|
*/
|
|
|
|
|
void wait();
|
|
|
|
|
/*! \brief Wake up a single thread waiting on this monitor.
|
|
|
|
|
*
|
|
|
|
|
* \note The monitor must be owned before calling notify().
|
|
|
|
|
*/
|
|
|
|
|
void notify();
|
|
|
|
|
/*! \brief Wake up all threads that are waiting on this monitor.
|
|
|
|
|
*
|
|
|
|
|
* \note The monitor must be owned before calling notifyAll().
|
|
|
|
|
*/
|
|
|
|
|
void notifyAll();
|
|
|
|
|
|
|
|
|
|
//! Return this lock's name.
|
|
|
|
|
const char* name() const { return name_; }
|
2014-07-04 16:17:05 -04:00
|
|
|
};
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
class ScopedLock : StackObject {
|
|
|
|
|
private:
|
|
|
|
|
Monitor* lock_;
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
public:
|
|
|
|
|
ScopedLock(Monitor& lock) : lock_(&lock) { lock_->lock(); }
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
ScopedLock(Monitor* lock) : lock_(lock) {
|
|
|
|
|
if (lock_) lock_->lock();
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
~ScopedLock() {
|
|
|
|
|
if (lock_) lock_->unlock();
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! @}
|
|
|
|
|
* @}
|
|
|
|
|
*/
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
inline bool Monitor::tryLock() {
|
|
|
|
|
Thread* thread = Thread::current();
|
|
|
|
|
assert(thread != NULL && "cannot lock() from (null)");
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
intptr_t ptr = contendersList_.load(std::memory_order_acquire);
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
if (unlikely((ptr & kLockBit) != 0)) {
|
|
|
|
|
if (recursive_ && thread == owner_) {
|
|
|
|
|
// Recursive lock: increment the lock count and return.
|
|
|
|
|
++lockCount_;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false; // Already locked!
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
if (unlikely(!contendersList_.compare_exchange_weak(
|
|
|
|
|
ptr, ptr | kLockBit, std::memory_order_acq_rel, std::memory_order_acquire))) {
|
|
|
|
|
return false; // We failed the CAS from unlocked to locked.
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
setOwner(thread); // cannot move above the CAS.
|
|
|
|
|
lockCount_ = 1;
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
return true;
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
inline void Monitor::lock() {
|
|
|
|
|
if (unlikely(!tryLock())) {
|
|
|
|
|
// The lock is contented.
|
|
|
|
|
finishLock();
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
// This is the beginning of the critical region. From now-on, everything
|
|
|
|
|
// executes single-threaded!
|
|
|
|
|
//
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
inline void Monitor::unlock() {
|
|
|
|
|
assert(isLocked() && owner_ == Thread::current() && "invariant");
|
|
|
|
|
|
|
|
|
|
if (recursive_ && --lockCount_ > 0) {
|
|
|
|
|
// was a recursive lock case, simply return.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setOwner(NULL);
|
|
|
|
|
|
|
|
|
|
// Clear the lock bit.
|
|
|
|
|
intptr_t ptr = contendersList_.load(std::memory_order_acquire);
|
|
|
|
|
while (!contendersList_.compare_exchange_weak(ptr, ptr & ~kLockBit, std::memory_order_acq_rel,
|
|
|
|
|
std::memory_order_acquire))
|
|
|
|
|
;
|
|
|
|
|
//
|
|
|
|
|
// We succeeded the CAS from locked to unlocked.
|
|
|
|
|
// This is the end of the critical region.
|
|
|
|
|
|
|
|
|
|
// Check if we have an on-deck thread that needs signaling.
|
|
|
|
|
intptr_t onDeck = onDeck_;
|
|
|
|
|
if (onDeck != 0) {
|
|
|
|
|
if ((onDeck & kLockBit) == 0) {
|
|
|
|
|
// Only signal if it is unmarked.
|
|
|
|
|
reinterpret_cast<Semaphore*>(onDeck)->post();
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
2017-04-13 13:56:38 -04:00
|
|
|
return; // We are done.
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
// We do not have an on-deck thread yet, we might have to walk the list in
|
|
|
|
|
// order to select the next onDeck_. Only one thread needs to fill onDeck_,
|
|
|
|
|
// so return if the list is empty or if the lock got acquired again (it's
|
|
|
|
|
// somebody else's problem now!)
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
intptr_t head = contendersList_;
|
|
|
|
|
if (head == 0 || (head & kLockBit) != 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
// Finish the unlock operation: find a thread to wake up.
|
|
|
|
|
finishUnlock();
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
} // namespace amd
|
2014-07-04 16:17:05 -04:00
|
|
|
|
|
|
|
|
#endif /*MONITOR_HPP_*/
|