2
0

initial commit

[ROCm/clr commit: 3694ab2ce8]
Este cometimento está contido em:
foreman
2014-07-04 16:17:05 -04:00
ascendente b0b5b33fcf
cometimento f80f2f233c
351 ficheiros modificados com 113713 adições e 1 eliminações
+677
Ver ficheiro
@@ -0,0 +1,677 @@
//
// Copyright (c) 2009 Advanced Micro Devices, Inc. All rights reserved.
//
/*! \file atomic.hpp
* \brief Declarations for Memory order access and Atomic operations.
*
* \author Laurent Morichetti (laurent.morichetti@amd.com)
* \date October 2008
*/
#ifndef ATOMIC_HPP_
#define ATOMIC_HPP_
#include "top.hpp"
#include <type_traits>
#ifdef _WIN32
# include <intrin.h>
#elif defined(ATI_ARCH_X86)
# include <emmintrin.h>
# include <xmmintrin.h>
#endif // !_WIN32
#include <utility>
namespace amd {
/*! \addtogroup Threads
* @{
*
* \defgroup MemOrder Memory ordering
* @{
*/
/*! \brief Memory order access operations.
*/
class MemoryOrder : AllStatic
{
public:
/*! \brief Execute a memory fence.
*
* Perform a serializing operation on loads and stores which guarantees
* that all memory operations dispatched prior to the fence will be
* globally visible before any other memory operation following the fence.
*/
static void fence() {
# if defined(ATI_ARCH_X86)
_mm_mfence();
# else // !ATI_ARCH_X86
__sync_synchronize();
# endif // !ATI_ARCH_X86
}
/*! \brief Execute a loads fence.
*
* Perform a serializing operation on loads which guarantees that all
* load from memory operations dispatched prior to the lfence will be
* globally visible before any other load following the lfence.
*/
static void lfence() {
# if defined(ATI_ARCH_X86)
_mm_lfence();
# else // !ATI_ARCH_X86
fence();
# endif // !ATI_ARCH_X86
}
/*! \brief Execute a stores fence.
*
* Perform a serializing operation on stores which guarantees that all
* store to memory operations dispatched prior to the sfence will be
* globally visible before any other store following the sfence.
*/
static void sfence() {
# if defined(ATI_ARCH_X86)
_mm_sfence();
# else // !ATI_ARCH_X86
fence();
# endif // !ATI_ARCH_X86
}
};
/*! @}
* \addtogroup Atomic Atomic Operations
* @{
*/
/*! \brief Static functions for atomic operations.
*/
class AtomicOperation : AllStatic
{
private:
//! Template to specialize atomic intrinsics on register size.
template <int N>
struct Intrinsics {
/*! \brief %Atomic add.
*
* Atomically add \a inc to \a *dest and return the prior value.
*/
template <typename T>
static inline T add(T increment, volatile T* dest);
/*! \brief %Atomic exchange.
*
* Atomically exchange value with *dest and return the prior value.
*/
template <typename T>
static inline T swap(T value, volatile T* dest);
/*! \brief %Atomic compare and exchange.
*
* Atomically compare and xchge value with *dest if *dest == compare.
* Return the prior value.
*/
template <typename T>
static inline T compareAndSwap(T compare, volatile T* dest, T value);
/*! \brief %Atomic increment.
*
* Atomically increment *dest and return the prior value.
*/
template <typename T>
static inline T increment(volatile T* dest);
/*! \brief %Atomic exchange.
*
* Atomically decrement *dest and return the prior value.
*/
template <typename T>
static inline T decrement(volatile T* dest);
/*! \brief %Atomic or.
*
* Atomically or \a mask to \a *dest and return the prior value.
*/
template <typename T>
static inline T _or(T mask, volatile T* dest);
/*! \brief %Atomic and.
*
* Atomically and \a mask to \a *dest and return the prior value.
*/
template <typename T>
static inline T _and(T mask, volatile T* dest);
};
public:
/*! \brief %Atomic add.
*
* Atomically add \a inc to \a *dest and return the prior value.
*/
template <typename T>
static T add(T inc, volatile T* dest)
{
return Intrinsics<sizeof(T)>::add((T) inc, dest);
}
/*! \brief %Atomic exchange.
*
* Atomically exchange value with *dest and return the prior value.
*/
template <typename T>
static T swap(T value, volatile T* dest)
{
return Intrinsics<sizeof(T)>::swap(value, dest);
}
/*! \brief %Atomic compare and exchange.
*
* Atomically compare and exchange value with *dest if *dest == compare.
* Return the prior value.
*/
template <typename T>
static T compareAndSwap(T compare, volatile T* dest, T value)
{
return Intrinsics<sizeof(T)>::compareAndSwap(compare, dest, value);
}
/*! \brief %Atomic increment.
*
* Atomically increment *dest and return the prior value.
*/
template <typename T>
static T increment(volatile T* dest)
{
return Intrinsics<sizeof(T)>::increment(dest);
}
/*! \brief %Atomic decrement.
*
* Atomically decrement *dest and return the prior value.
*/
template <typename T>
static T decrement(volatile T* dest)
{
return Intrinsics<sizeof(T)>::decrement(dest);
}
/*! \brief %Atomic or.
*
* Atomically or \a mask to \a *dest and return the prior value.
*/
template <typename T>
static T _or(T mask, volatile T* dest)
{
return Intrinsics<sizeof(T)>::_or((T) mask, dest);
}
/*! \brief %Atomic and.
*
* Atomically or \a mask to \a *dest and return the prior value.
*/
template <typename T>
static T _and(T mask, volatile T* dest)
{
return Intrinsics<sizeof(T)>::_and((T) mask, dest);
}
};
/*@}*/
#if defined(_MSC_VER)
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<4>::add(T increment, volatile T* dest)
{
return (T)_InterlockedExchangeAdd(
(volatile long*)dest, (long)increment);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<4>::swap(T value, volatile T* dest)
{
return (T)_InterlockedExchange(
(volatile long*)dest, (long)value);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<4>::compareAndSwap(
T compare, volatile T* dest, T value)
{
return (T)_InterlockedCompareExchange(
(volatile long*)dest, (long)value, (long)compare);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<4>::increment(volatile T* dest)
{
return (T)(_InterlockedIncrement((volatile long*)dest) - 1L);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<4>::decrement(volatile T* dest)
{
return (T)(_InterlockedDecrement((volatile long*)dest) + 1L);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<4>::_or(T mask, volatile T* dest)
{
return (T)_InterlockedOr(
(volatile long*)dest, (long)mask);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<4>::_and(T mask, volatile T* dest)
{
return (T)_InterlockedAnd(
(volatile long*)dest, (long)mask);
}
#ifdef _WIN64
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<8>::add(T increment, volatile T* dest)
{
return (T)_InterlockedExchangeAdd64(
(volatile __int64*)dest, (__int64)increment);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<8>::swap(T value, volatile T* dest)
{
return (T)_InterlockedExchange64(
(volatile __int64*)dest, (__int64)value);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<8>::compareAndSwap(
T compare, volatile T* dest, T value)
{
return (T)_InterlockedCompareExchange64(
(volatile __int64*)dest, (__int64)value, (__int64)compare);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<8>::increment(volatile T* dest)
{
return (T)(_InterlockedIncrement64((volatile __int64*)dest) - 1LL);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<8>::decrement(volatile T* dest)
{
return (T)(_InterlockedDecrement64((volatile __int64*)dest) + 1LL);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<8>::_or(T mask, volatile T* dest)
{
return (T)_InterlockedOr64(
(volatile long*)dest, (long)mask);
}
template <>
template <typename T>
inline T
AtomicOperation::Intrinsics<8>::_and(T mask, volatile T* dest)
{
return (T)_InterlockedAnd64(
(volatile long*)dest, (long)mask);
}
#endif // _LP64
#elif defined(__GNUC__)
template <int N>
template <typename T>
inline T
AtomicOperation::Intrinsics<N>::add(T inc, volatile T* dest)
{
return __sync_fetch_and_add(dest, inc);
}
template<int N>
template <typename T>
inline T
AtomicOperation::Intrinsics<N>::swap(T value, volatile T* dest)
{
return __sync_lock_test_and_set(dest, value);
}
template <int N>
template <typename T>
inline T
AtomicOperation::Intrinsics<N>::compareAndSwap(
T compare, volatile T* dest, T value)
{
return __sync_val_compare_and_swap(dest, compare, value);
}
template<int N>
template <typename T>
inline T
AtomicOperation::Intrinsics<N>::increment(volatile T* dest)
{
return add(T(1), dest);
}
template<int N>
template <typename T>
inline T
AtomicOperation::Intrinsics<N>::decrement(volatile T* dest)
{
return add(T(-1), dest);
}
template <int N>
template <typename T>
inline T
AtomicOperation::Intrinsics<N>::_or(T mask, volatile T* dest)
{
return __sync_fetch_and_or(dest, mask);
}
template <int N>
template <typename T>
inline T
AtomicOperation::Intrinsics<N>::_and(T mask, volatile T* dest)
{
return __sync_fetch_and_and(dest, mask);
}
#else
# error Unimplemented
#endif
/*! \addtogroup Atomic Atomic Operations
* @{
*/
/*! \brief A variable of type T with atomic properties.
*/
template <typename T>
class Atomic
{
private:
typedef typename std::remove_volatile<typename std::remove_pointer<
typename std::remove_reference<T>::type>::type>::type value_type;
typename std::add_volatile<T>::type value_; //!< \brief The variable.
public:
//! Construct a new %Atomic variable of type T.
Atomic() : value_(T(0)) {}
//! Construct a new %Atomic variable of type T from \a value.
Atomic(T value) : value_(value) {}
//! Construct a new %Atomic variable of type T from another %Atomic.
Atomic(const Atomic<T>& atomic) : value_(atomic.value_) { }
//! Copy value into this %Atomic variable.
Atomic<T>& operator = (T value)
{
value_ = value;
return *this;
}
//! Return the %Atomic variable value.
operator T () const { return T(value_); }
//! Return the %Atomic variable value.
T operator ->() const { return T(value_); }
//! Return the %Atomic variable's address.
typename std::add_pointer<typename std::add_volatile<value_type>::type>::
type operator &() { return &value_; }
//! Atomically add \a inc to this variable.
Atomic<T>& operator += (value_type inc)
{
if (std::is_pointer<T>::value) {
inc *= sizeof(typename std::remove_pointer<T>::type);
}
AtomicOperation::add(inc, &value_);
return *this;
}
//! Atomically subtract \a inc to this variable.
Atomic<T>& operator -= (value_type inc)
{
value_type modifier = 0;
if (std::is_pointer<T>::value) {
inc *= sizeof(typename std::remove_pointer<T>::type);
}
AtomicOperation::add(modifier - inc, &value_);
return *this;
}
//! Atomically OR \a value to this variable.
Atomic<T>& operator |= (value_type mask)
{
AtomicOperation::_or(mask, &value_);
return *this;
}
//! Atomically AND \a value to this variable.
Atomic<T>& operator &= (value_type mask)
{
AtomicOperation::_and(mask, &value_);
return *this;
}
//! Atomically increment this variable and return its new value.
typename std::remove_reference<T>::type operator ++ ()
{
if (std::is_pointer<T>::value) {
value_type inc = static_cast<value_type>(
sizeof(typename std::remove_pointer<T>::type));
return AtomicOperation::add(inc, &value_) + 1;
}
else {
return AtomicOperation::increment(&value_) + 1;
}
}
//! Atomically decrement this variable and return its new value.
typename std::remove_reference<T>::type operator -- ()
{
if (std::is_pointer<T>::value) {
value_type inc = static_cast<value_type>(-
static_cast<typename std::make_signed<value_type>::type>(
sizeof(typename std::remove_pointer<T>::type)));
return AtomicOperation::add(inc, &value_) - 1;
}
else {
return AtomicOperation::decrement(&value_) - 1;
}
}
//! Atomically increment this variable and return its previous value.
typename std::remove_reference<T>::type operator ++ (int)
{
if (std::is_pointer<T>::value) {
value_type inc = static_cast<value_type>(
sizeof(typename std::remove_pointer<T>::type));
return AtomicOperation::add(inc, &value_);
}
else {
return AtomicOperation::increment(&value_);
}
}
//! Atomically decrement this variable and return its previous value.
T operator -- (int)
{
if (std::is_pointer<T>::value) {
value_type inc = static_cast<value_type>(-
static_cast<typename std::make_signed<value_type>::type>(
sizeof(typename std::remove_pointer<T>::type)));
return AtomicOperation::add(inc, &value_);
}
else {
return AtomicOperation::decrement(&value_);
}
}
/*! \brief Atomically compare this variable with \a compare and set
* to value if equals
*/
bool compareAndSet(T compare, T value)
{
return compare == AtomicOperation::compareAndSwap(
compare, &value_, value);
}
//! Atomically set this variable to \a value and return its previous value.
T swap(T value)
{
return AtomicOperation::swap(value, &value_);
}
/*! \brief Execute a stores fence followed by a store to this variable.
*
* This storeRelease operation ensures that all store to memory operations
* preceding this function will be globally visible before the update to
* this variable's value.
*/
void storeRelease(T value)
{
MemoryOrder::fence();
value_ = value;
}
/*! \brief Execute a load from this variable followed by a loads fence.
*
* This loadAcquire operation ensures that all load from memory operations
* following this function will be globally visible after the read from
* this variable's value.
*/
T loadAcquire() const
{
T value = value_;
MemoryOrder::fence();
return value;
}
};
//! Helper function to tie an Atomic<T&> to a variable of type T.
template <typename T>
inline Atomic<T&>
make_atomic(T& t)
{
return Atomic<T&>(t);
}
template <typename T>
class AtomicMarkableReference
{
private:
static const intptr_t kMarkBitMask = 0x1;
private:
Atomic<T*> reference_;
private:
static intptr_t markMask(bool mark)
{
return mark ? kMarkBitMask : intptr_t(0);
}
public:
AtomicMarkableReference()
: reference_(NULL)
{ }
AtomicMarkableReference(T* ptr, bool mark = false)
: reference_((T*)((intptr_t) ptr | markMask(mark)))
{ }
bool compareAndSet(
T* expectedPtr, T* newPtr,
bool expectedMark, bool newMark)
{
return reference_.compareAndSet(
(T*)((intptr_t) expectedPtr | markMask(expectedMark)),
(T*)((intptr_t) newPtr | markMask(newMark)));
}
std::pair<T*,bool> swap(T* newPtr, bool newMark)
{
T* prev = reference_.swap(
(T*)((intptr_t) newPtr | markMask(newMark)));
return std::make_pair(
(T*) ((intptr_t) prev & ~kMarkBitMask),
((intptr_t) prev & kMarkBitMask) != 0);
}
bool tryMark(T* expectedPtr, bool newMark)
{
T* current = reference_;
if (((intptr_t) current & ~kMarkBitMask) != (intptr_t) expectedPtr) {
return false;
}
bool currentMark = ((intptr_t) current & kMarkBitMask) != 0;
return currentMark == newMark || reference_.compareAndSet(current,
(T*)((intptr_t) expectedPtr | markMask(newMark)));
}
bool isMarked() const
{
return ((intptr_t)(T*) reference_ & kMarkBitMask) != 0;
}
std::pair<T*,bool> get() const
{
T* current = reference_;
return std::make_pair(
(T*) ((intptr_t) current & ~kMarkBitMask),
((intptr_t) current & kMarkBitMask) != 0);
}
T* getReference() const
{
return (T*) ((intptr_t)(T*) reference_ & ~kMarkBitMask);
}
void set(T* ptr, bool mark)
{
reference_ = (T*)((intptr_t) ptr | markMask(mark));
}
};
/*! @}
* @}
*/
} // namespace amd
#endif /*ATOMIC_HPP_*/
+313
Ver ficheiro
@@ -0,0 +1,313 @@
//
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
//
#include "thread/monitor.hpp"
#include "thread/atomic.hpp"
#include "thread/semaphore.hpp"
#include "thread/thread.hpp"
#include "utils/util.hpp"
#include <cstring>
#include <tuple>
#include <utility>
namespace amd {
Monitor::Monitor(const char* name, bool recursive) :
contendersList_(NULL), onDeck_(NULL), waitersList_(NULL),
owner_(NULL), recursive_(recursive)
{
const size_t maxNameLen = sizeof(name_);
if (name == NULL) {
const char* unknownName = "@unknown@";
assert(sizeof(unknownName) < maxNameLen && "just checking");
strcpy(name_, unknownName);
}
else {
strncpy(name_, name, maxNameLen - 1);
name_[maxNameLen - 1] = '\0';
}
}
bool
Monitor::trySpinLock()
{
if (tryLock()) {
return true;
}
for (int s = kMaxSpinIter; s > 0; --s) {
// First, be SMT friendly
if (s >= (kMaxSpinIter - kMaxReadSpinIter)) {
Os::spinPause();
}
// and then SMP friendly
else {
Thread::yield();
}
if (!isLocked()) {
return tryLock();
}
}
// We could not acquire the lock in the spin loop.
return false;
}
void
Monitor::finishLock()
{
Thread* thread = Thread::current();
assert(thread != NULL && "cannot lock() from (null)");
if (trySpinLock()) {
return; // We succeeded, we are done.
}
/* The lock is contended. Push the thread's semaphore onto
* the contention list.
*/
Semaphore& sem = thread->lockSemaphore();
sem.reset();
LinkedNode newHead;
newHead.setItem(&sem);
while (true) {
LinkedNode* head; bool isLocked;
// The assumption is that lockWord is locked. Make sure we do not
// continue unless the lock bit is set.
std::tie(head, isLocked) = contendersList_.get();
if (!isLocked) {
if (tryLock()) {
return;
}
continue;
}
// Set the new contention list head if lockWord is unchanged.
newHead.setNext(head);
if (contendersList_.compareAndSet(head, &newHead, kLocked, kLocked)) {
break;
}
// We failed the CAS. yield/pause before trying again.
Thread::yield();
}
int32_t spinCount = 0;
// Go to sleep until we become the on-deck thread.
while (onDeck_.getReference() != &sem) {
// First, be SMT friendly
if (spinCount < kMaxReadSpinIter) {
Os::spinPause();
}
// and then SMP friendly
else if (spinCount < kMaxSpinIter) {
Thread::yield();
}
// now go to sleep
else {
sem.wait();
}
spinCount++;
}
spinCount = 0;
//
// From now-on, we are the on-deck thread. It will stay that way until
// we successfuly acquire the lock.
//
while (true) {
assert(onDeck_.getReference() == &sem && "just checking");
if (tryLock()) {
break;
}
// Somebody beat us to it. Since we are on-deck, we can just go
// back to sleep.
// First, be SMT friendly
if (spinCount < kMaxReadSpinIter) {
Os::spinPause();
}
// and then SMP friendly
else if (spinCount < kMaxSpinIter) {
Thread::yield();
}
// now go to sleep
else {
sem.wait();
}
spinCount++;
}
assert(newHead.next() == NULL && "Should not be linked");
onDeck_ = NULL;
}
void
Monitor::finishUnlock()
{
// If we get here, it means that there might be a thread in the contention
// list waiting to acquire the lock. We need to select a successor and
// place it on-deck.
while (true) {
// Grab the onDeck_ microlock to protect the next loop (make sure only
// one semaphore is removed from the contention list).
//
if (!onDeck_.compareAndSet(NULL, NULL, kUnlocked, kLocked)) {
return; // Somebody else has the microlock, let him select onDeck_
}
LinkedNode* head; bool isLocked;
while (true) {
std::tie(head, isLocked) = contendersList_.get();
if (head == NULL) {
break; // There's nothing else to do.
}
if (isLocked) {
// Somebody could have acquired then released the lock
// and failed to grab the onDeck_ microlock.
head = NULL;
break;
}
if (contendersList_.compareAndSet(
head, head->next(), kUnlocked, kUnlocked)) {
#ifdef ASSERT
head->setNext(NULL);
#endif // ASSERT
break;
}
}
Semaphore* sem = (head != NULL) ? head->item() : NULL;
onDeck_ = sem;
MemoryOrder::fence();
//
// Release the onDeck_ microlock (end of critical region);
if (sem != NULL) {
sem->post();
return;
}
// We do not have an on-deck thread (sem == NULL). Return if
// the contention list is empty or if the lock got acquired again.
std::tie(head, isLocked) = contendersList_.get();
if (isLocked || head == NULL) {
return;
}
}
}
void
Monitor::wait()
{
Thread* thread = Thread::current();
assert(isLocked() && owner_ == thread && "just checking");
// Add the thread's resume semaphore to the list.
Semaphore& suspend = thread->suspendSemaphore();
suspend.reset();
LinkedNode newHead;
newHead.setItem(&suspend);
newHead.setNext(waitersList_);
waitersList_ = &newHead;
// Preserve the lock count (for recursive mutexes)
uint32_t lockCount = lockCount_;
lockCount_ = 1;
// Release the lock and go to sleep.
unlock();
// Go to sleep until we become the on-deck thread.
int32_t spinCount = 0;
while (onDeck_.getReference() != &suspend) {
// First, be SMT friendly
if (spinCount < kMaxReadSpinIter) {
Os::spinPause();
}
// and then SMP friendly
else if (spinCount < kMaxSpinIter) {
Thread::yield();
}
// now go to sleep
else {
suspend.wait();
}
spinCount++;
}
spinCount = 0;
while (true) {
assert(onDeck_.getReference() == &suspend && "just checking");
if (trySpinLock()) {
break;
}
// Somebody beat us to it. Since we are on-deck, we can just go
// back to sleep.
// First, be SMT friendly
if (spinCount < kMaxReadSpinIter) {
Os::spinPause();
}
// and then SMP friendly
else if (spinCount < kMaxSpinIter) {
Thread::yield();
}
// now go to sleep
else {
suspend.wait();
}
spinCount++;
}
// Restore the lock count (for recursive mutexes)
lockCount_ = lockCount;
onDeck_ = NULL;
MemoryOrder::fence();
}
void
Monitor::notify()
{
assert(isLocked() && owner_ == Thread::current() && "just checking");
LinkedNode* waiter = waitersList_;
if (waiter == NULL) {
return;
}
// Dequeue a waiter from the wait list and add it to the contention list.
waitersList_ = waiter->next();
while (true) {
LinkedNode* node = contendersList_.getReference();
waiter->setNext(node);
if (contendersList_.compareAndSet(node, waiter, kLocked, kLocked)) {
break;
}
}
}
void
Monitor::notifyAll()
{
// NOTE: We could CAS the whole list in 1 shot but this is
// not critical code. Optimize this if it becomes hot.
while (waitersList_ != NULL) {
notify();
}
}
} // namespace amd
+233
Ver ficheiro
@@ -0,0 +1,233 @@
//
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
//
#ifndef MONITOR_HPP_
#define MONITOR_HPP_
#include "top.hpp"
#include "atomic.hpp"
#include "thread/semaphore.hpp"
#include "thread/thread.hpp"
#include <tuple>
#include <utility>
namespace amd {
/*! \addtogroup Threads
* @{
*
* \addtogroup Synchronization
* @{
*/
class Monitor : public HeapObject
{
typedef SimplyLinkedNode<Semaphore*,StackObject> LinkedNode;
private:
static const bool kUnlocked = false;
static const bool kLocked = true;
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.
*/
AtomicMarkableReference<LinkedNode> contendersList_;
//! The Mutex's name
char name_[64];
//! Semaphore of the next thread to contend for the lock.
AtomicMarkableReference<Semaphore> 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_.isMarked(); }
//! 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_; }
};
class ScopedLock : StackObject
{
private:
Monitor* lock_;
public:
ScopedLock(Monitor& lock)
: lock_(&lock)
{
lock_->lock();
}
ScopedLock(Monitor* lock)
: lock_(lock)
{
if (lock_) lock_->lock();
}
~ScopedLock()
{
if (lock_) lock_->unlock();
}
};
/*! @}
* @}
*/
inline bool
Monitor::tryLock()
{
Thread* thread = Thread::current();
assert(thread != NULL && "cannot lock() from (null)");
LinkedNode* ptr; bool isLocked;
std::tie(ptr, isLocked) = contendersList_.get();
if (unlikely(isLocked)) {
if (recursive_ && thread == owner_) {
// Recursive lock: increment the lock count and return.
++lockCount_;
return true;
}
return false; // Already locked!
}
if (unlikely(!contendersList_.compareAndSet(
ptr, ptr, kUnlocked, kLocked))) {
return false; // We failed the CAS from unlocked to locked.
}
setOwner(thread); // cannot move above the CAS.
lockCount_ = 1;
return true;
}
inline void
Monitor::lock()
{
if (unlikely(!tryLock())) {
// The lock is contented.
finishLock();
}
// This is the beginning of the critical region. From now-on, everything
// executes single-threaded!
//
}
inline void
Monitor::unlock()
{
assert(isLocked() && owner_ == Thread::current() && "invariant");
if (recursive_ && --lockCount_ > 0) {
// was a recursive lock case, simply return.
return;
}
setOwner(NULL);
while (true) {
LinkedNode* ptr = contendersList_.getReference();
// Clear the lock bit.
if (contendersList_.compareAndSet(ptr, ptr, kLocked, kUnlocked)) {
break; // 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.
Semaphore* onDeck; bool isMarked;
std::tie(onDeck, isMarked) = onDeck_.get();
if (onDeck != NULL) {
if (!isMarked) {
// Only signal if it is unmarked.
onDeck->post();
}
return; // We are done.
}
// 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!)
LinkedNode* head; bool isLocked;
std::tie(head, isLocked) = contendersList_.get();
if (isLocked || head == NULL) {
return;
}
// Finish the unlock operation: find a thread to wake up.
finishUnlock();
}
} // namespace amd
#endif /*MONITOR_HPP_*/
+96
Ver ficheiro
@@ -0,0 +1,96 @@
//
// Copyright (c) 2008,2010 Advanced Micro Devices, Inc. All rights reserved.
//
#include "thread/semaphore.hpp"
#include "thread/atomic.hpp"
#include "thread/thread.hpp"
#if defined(_WIN32) || defined(__CYGWIN__)
# include <windows.h>
#else // !_WIN32
# include <semaphore.h>
# include <errno.h>
#endif // !_WIN32
namespace amd {
Semaphore::Semaphore()
: state_(0)
{
#ifdef _WIN32
handle_ = static_cast<void*>(CreateSemaphore(NULL, 0, LONG_MAX, NULL));
assert(handle_ != NULL && "CreateSemaphore failed");
#else // !_WIN32
if (sem_init(&sem_, 0, 0) != 0) {
fatal("sem_init() failed");
}
#endif // !_WIN32
}
Semaphore::~Semaphore()
{
#ifdef _WIN32
if (!CloseHandle(static_cast<HANDLE>(handle_))) {
fatal("CloseHandle() failed");
}
#else // !_WIN32
if (sem_destroy(&sem_) != 0) {
fatal("sem_destroy() failed");
}
#endif // !WIN32
}
void
Semaphore::post()
{
int state;
while (true) {
state = state_;
if (state > 0) {
// Do a load acquire.
MemoryOrder::fence();
if (state == state_) {
return;
}
continue;
}
if (state_.compareAndSet(state, state+1)) {
break;
}
}
if (state < 0) {
// We have threads waiting on this event.
#ifdef _WIN32
ReleaseSemaphore(static_cast<HANDLE>(handle_), 1, NULL);
#else // !_WIN32
if (0 != sem_post(&sem_)) {
fatal("sem_post() failed");
}
#endif // !_WIN32
}
}
void
Semaphore::wait()
{
if (state_-- > 0) {
return;
}
#ifdef _WIN32
if (WAIT_OBJECT_0 != WaitForSingleObject(
static_cast<HANDLE>(handle_), INFINITE)) {
fatal("WaitForSingleObject failed");
}
#else // !_WIN32
while (0 != sem_wait(&sem_)) {
if (EINTR != errno) {
fatal("sem_wait() failed");
}
}
#endif // !_WIN32
}
} // namespace amd
+65
Ver ficheiro
@@ -0,0 +1,65 @@
//
// Copyright (c) 2008,2010 Advanced Micro Devices, Inc. All rights reserved.
//
#ifndef SEMAPHORE_HPP_
#define SEMAPHORE_HPP_
#include "top.hpp"
#include "thread/atomic.hpp"
#include "utils/util.hpp"
#if defined(__linux__)
# include <semaphore.h>
#endif /*linux*/
namespace amd {
/*! \addtogroup Threads
* @{
*
* \addtogroup Synchronization
* @{
*/
class Thread;
//! \brief Counting semaphore
class Semaphore : public HeapObject
{
private:
Atomic<int> state_; //!< This semaphore's value.
#ifdef _WIN32
void* handle_; //!< The semaphore object's handle.
char padding_[64-sizeof(void*)-sizeof(Atomic<int>)];
#else // !_WIN32
sem_t sem_; //!< The semaphore object's identifier.
char padding_[64-sizeof(sem_t)-sizeof(Atomic<int>)];
#endif /*!_WIN32*/
public:
Semaphore();
~Semaphore();
//! \brief Decrement this semaphore
void wait();
//! \brief Increment this semaphore
void post();
//! \brief Reset this semaphore.
void reset()
{
state_.swap(0);
}
};
/*! @}
* @}
*/
} // namespace amd
#endif /*SEMAPHORE_HPP_*/
+191
Ver ficheiro
@@ -0,0 +1,191 @@
//
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
//
#include "thread/thread.hpp"
#include "thread/semaphore.hpp"
#include "thread/monitor.hpp"
#include "os/os.hpp"
#if defined(_WIN32) || defined(__CYGWIN__)
# include <windows.h>
#endif // _WIN32
namespace amd {
HostThread::HostThread()
: Thread("HostThread", 0, false)
{
setCurrent();
Os::currentStackInfo(&stackBase_, &stackSize_);
setState(RUNNABLE);
}
void
Thread::create()
{
created_ = new Semaphore();
lock_ = new Semaphore();
suspend_ = new Semaphore();
selfSuspendLock_ = new Monitor();
data_ = NULL;
handle_ = NULL;
setState(CREATED);
}
Thread::Thread(const std::string& name, size_t stackSize, bool spawn)
: handle_(NULL), name_(name), stackSize_(stackSize)
{
create();
if (!spawn) return;
if ((handle_ = Os::createOsThread(this))) {
// Now we need to wait for Thread::main to report back.
while (state() != Thread::INITIALIZED) {
created_->wait();
}
}
}
Thread::~Thread()
{
#if defined(_WIN32)
if (handle_ != NULL) {
::CloseHandle((HANDLE) handle_);
}
#endif
delete created_;
delete lock_;
delete suspend_;
delete selfSuspendLock_;
}
void*
Thread::main()
{
#ifdef DEBUG
Os::setCurrentThreadName(name().c_str());
#endif // DEBUG
Os::currentStackInfo(&stackBase_, &stackSize_);
setCurrent();
// Notify the parent thread that we are up and running.
{
ScopedLock sl(selfSuspendLock_);
setState(INITIALIZED);
created_->post();
selfSuspendLock_->wait();
}
if (state() == RUNNABLE) {
run(data_);
}
setState(FINISHED);
return NULL;
}
bool
Thread::start(void* data)
{
if (state() != INITIALIZED) {
return false;
}
data_ = data;
{
ScopedLock sl(selfSuspendLock_);
setState(RUNNABLE);
selfSuspendLock_->notify();
}
return true;
}
void
Thread::resume()
{
ScopedLock sl(selfSuspendLock_);
selfSuspendLock_->notify();
}
#if defined(__linux__)
namespace details {
__thread Thread* thread_ __attribute__((tls_model("initial-exec")));
} // namespace details
void
Thread::registerStack(address base, address top)
{
// Nothing to do.
}
void
Thread::setCurrent()
{
details::thread_ = this;
}
#elif defined(_WIN32)
namespace details {
#if defined(USE_DECLSPEC_THREAD)
__declspec(thread) Thread* thread_;
#else // !USE_DECLSPEC_THREAD
DWORD threadIndex_ = TlsAlloc();
#endif // !USE_DECLSPEC_THREAD
} // namespace details
void
Thread::registerStack(address base, address top)
{
// Nothing to do.
}
void
Thread::setCurrent()
{
#if defined(USE_DECLSPEC_THREAD)
details::thread_ = this;
#else // !USE_DECLSPEC_THREAD
TlsSetValue(details::threadIndex_, this);
#endif // !USE_DECLSPEC_THREAD
}
#endif
bool
Thread::init()
{
static bool initialized_ = false;
// We could use InitOnceExecuteOnce/pthread_once here:
if (initialized_) {
return true;
}
initialized_ = true;
// Register the main thread
return NULL != new HostThread();
}
void
Thread::tearDown()
{
#if defined(_WIN32) && !defined(USE_DECLSPEC_THREAD)
if (details::threadIndex_ != TLS_OUT_OF_INDEXES) {
TlsFree(threadIndex_);
}
#endif // _WIN32 && !USE_DECLSPEC_THREAD
}
} // namespace amd
+228
Ver ficheiro
@@ -0,0 +1,228 @@
//
// Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
//
#ifndef THREAD_HPP_
#define THREAD_HPP_
#include "top.hpp"
#include "thread/semaphore.hpp"
#include "os/os.hpp"
#include <string>
#if defined(_WIN32)
# define USE_DECLSPEC_THREAD 1
# if !defined(USE_DECLSPEC_THREAD)
# include <windows.h>
# endif /*!USE_DECLSPEC_THREAD*/
#endif /*_WIN32*/
namespace amd {
/*! \addtogroup Threads Threading package
* @{
*
* \addtogroup OsThread Native Threads
* @{
*/
class Monitor;
class Thread : public HeapObject
{
friend const void* Os::createOsThread(Thread*);
public:
enum ThreadState
{
CREATED,
INITIALIZED,
RUNNABLE,
SUSPENDED,
FINISHED,
FAILED
};
private:
//! System thread handle.
const void* handle_;
//! The thread's name.
const std::string name_;
//! Current running state.
volatile ThreadState state_;
//! The argument passed to run()
void* data_;
//! \cond ignore
Semaphore* created_; //!< To notify the parent thread.
Semaphore* lock_; //!< For mutex support (during contention).
Semaphore* suspend_; //!< For wait/suspend support.
//! \endcond
Monitor* selfSuspendLock_; //!< For self suspend/resume.
protected:
address stackBase_; //!< Main stack base.
size_t stackSize_; //!< Main stack size.
private:
/*! \brief The start wrapper for all newly create threads.
* This is called from the pthread_create start_thread.
*/
static void* entry(Thread* thread);
/*! \brief Thread main (called from the main function).
* Setup the thread for running and wait for the semaphore to be signaled.
*/
void* main();
//! The entry point for this thread.
virtual void run(void* data) = 0;
protected:
//! Bring this thread to the created state.
void create();
//! Set the current thread state.
void setState(ThreadState state) { state_ = state; }
//! Set the thread-local _thread variable (used by current()).
void setCurrent();
//! Register the given memory region as a valid stack.
void registerStack(address base, address top);
/*! \brief Construct a new thread.
* If \a spawn is false, do not create a new OS thread, instead,
* bind to the currently running on.
*/
explicit Thread(
const std::string& name,
size_t stackSize = 0 /*use system default*/,
bool spawn = true /* create a new Os::thread */);
public:
//! Return the currently running thread instance.
static inline Thread* current();
//! Initialize the OsThread package.
static bool init();
//! Tear down the OsThread package.
static void tearDown();
//! Destroy this thread.
virtual ~Thread();
//! Return the thread's name
const std::string& name() const { return name_; }
//! Get the system thread handle.
const void* handle() const { return handle_; }
//! Start the thread execution
bool start(void *data = NULL);
//! Resume the thread
void resume();
//! Return true is this is the host thread.
virtual bool isHostThread() const { return false; }
//! Get the current thread state.
ThreadState state() const { return state_; }
//! Return this thread's stack base.
address stackBase() const { return stackBase_; }
//! Return this thread's stack size.
size_t stackSize() const { return stackSize_; }
//! Return this thread's stack bottom.
address stackBottom() const { return stackBase() - stackSize(); }
//! Return this thread's contend semaphore.
Semaphore& lockSemaphore() const { return *lock_; }
//! Return this thread's resume semaphore.
Semaphore& suspendSemaphore() const { return *suspend_; }
//! Set this thread's affinity to the given cpu.
void setAffinity(uint cpu_id) const
{
Os::setThreadAffinity(handle_, cpu_id);
}
//! Set this thread's affinity to the given cpu mask.
void setAffinity(const Os::ThreadAffinityMask& mask) const
{
Os::setThreadAffinity(handle_, mask);
}
//! Yield to threads of the same priority of higher
static void yield()
{
Os::yield();
}
};
class HostThread : public Thread
{
private:
//! A HostThread does not have a run function
virtual void run(void* data) { ShouldNotCallThis(); }
public:
//! Construct a new HostThread
HostThread();
//! Return true is this is the host thread.
bool isHostThread() const { return true; };
};
/*! @}
* @}
*/
namespace details {
#if defined(__linux__)
extern __thread Thread* thread_ __attribute__((tls_model("initial-exec")));
static inline Thread*
currentThread()
{
return thread_;
}
#elif defined(_WIN32)
#if defined(USE_DECLSPEC_THREAD)
extern __declspec(thread) Thread* thread_;
#else // !USE_DECLSPEC_THREAD
extern DWORD threadIndex_;
#endif // !USE_DECLSPEC_THREAD
static inline Thread*
currentThread()
{
#if defined(USE_DECLSPEC_THREAD)
return thread_;
#else // !USE_DECLSPEC_THREAD
return (Thread*) TlsGetValue(threadIndex_);
#endif // !USE_DECLSPEC_THREAD
}
#endif // _WIN32
} // namespace details
inline Thread*
Thread::current()
{
return details::currentThread();
}
} // namespace amd
#endif /*THREAD_HPP_*/