Arquivos
hhvm/hphp/util/mutex.h
T
2013-02-19 06:57:54 -08:00

384 linhas
9.4 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef __MUTEX_H__
#define __MUTEX_H__
#include <util/assertions.h>
#include <util/util.h>
#include <pthread.h>
#include <time.h>
#include <tbb/concurrent_hash_map.h>
#include "util/rank.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
template <bool enableAssertions>
class BaseMutex {
private:
#ifdef DEBUG
static const int kMagic = 0xba5eba11;
int m_magic;
Rank m_rank;
// m_owner/m_hasOwner for keeping track of lock ownership, useful for debugging
pthread_t m_owner;
unsigned int m_acquires;
bool m_recursive;
bool m_hasOwner;
#endif
inline void recordAcquisition() {
#ifdef DEBUG
if (enableAssertions) {
assert(!m_hasOwner ||
pthread_equal(m_owner, pthread_self()));
assert(m_acquires == 0 ||
pthread_equal(m_owner, pthread_self()));
pushRank(m_rank);
m_hasOwner = true;
m_owner = pthread_self();
m_acquires++;
assert(m_recursive || m_acquires == 1);
}
#endif
}
inline void invalidateOwner() {
#ifdef DEBUG
if (enableAssertions) {
m_hasOwner = false;
m_acquires = 0;
}
#endif
}
inline void recordRelease() {
#ifdef DEBUG
if (enableAssertions) {
popRank(m_rank);
assertOwnedBySelf();
assert(m_acquires > 0);
if (--m_acquires == 0) {
m_hasOwner = false;
}
}
#endif
}
public:
inline void assertNotOwned() const {
#ifdef DEBUG
if (enableAssertions) {
assert(!m_hasOwner);
assert(m_acquires == 0);
}
#endif
}
inline void assertOwnedBySelf() const {
#ifdef DEBUG
if (enableAssertions) {
assert(m_hasOwner);
assert(pthread_equal(m_owner, pthread_self()));
assert(m_acquires > 0);
}
#endif
}
public:
BaseMutex(bool recursive = true, Rank r = RankUnranked) {
pthread_mutexattr_init(&m_mutexattr);
if (recursive) {
pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_RECURSIVE);
} else {
#if defined(__APPLE__)
pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_DEFAULT);
#else
pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_ADAPTIVE_NP);
#endif
}
pthread_mutex_init(&m_mutex, &m_mutexattr);
#ifdef DEBUG
m_rank = r;
m_magic = kMagic;
invalidateOwner();
m_recursive = recursive;
#endif
}
~BaseMutex() {
#ifdef DEBUG
assert(m_magic == kMagic);
#endif
assertNotOwned();
pthread_mutex_destroy(&m_mutex);
pthread_mutexattr_destroy(&m_mutexattr);
#ifdef DEBUG
m_magic = ~m_magic;
#endif
}
bool tryLock() {
#ifdef DEBUG
assert(m_magic == kMagic);
#endif
bool success = !pthread_mutex_trylock(&m_mutex);
if (success) {
recordAcquisition();
assertOwnedBySelf();
}
return success;
}
bool tryLockWait(long long ns) {
#ifdef DEBUG
assert(m_magic == kMagic);
#endif
struct timespec delta;
delta.tv_sec = 0;
delta.tv_nsec = ns;
bool success = !pthread_mutex_timedlock(&m_mutex, &delta);
if (success) {
recordAcquisition();
assertOwnedBySelf();
}
return success;
}
void lock() {
#ifdef DEBUG
assert(m_magic == kMagic);
checkRank(m_rank);
#endif
UNUSED int ret = pthread_mutex_lock(&m_mutex);
assert(ret == 0);
recordAcquisition();
assertOwnedBySelf();
}
void unlock() {
#ifdef DEBUG
assert(m_magic == kMagic);
#endif
recordRelease();
UNUSED int ret = pthread_mutex_unlock(&m_mutex);
assert(ret == 0);
}
private:
BaseMutex(const BaseMutex &); // suppress
BaseMutex &operator=(const BaseMutex &); // suppress
protected:
pthread_mutexattr_t m_mutexattr;
pthread_mutex_t m_mutex;
};
/**
* Standard recursive mutex, which can be used for condition variables.
* This mutex does not support enabling assertions
*/
class Mutex : public BaseMutex<false> {
public:
Mutex(bool recursive = true, Rank rank = RankUnranked) :
BaseMutex<false>(recursive, rank) {}
pthread_mutex_t &getRaw() { return m_mutex; }
};
/**
* Simple recursive mutex, which does not expose the underlying raw
* pthread_mutex_t. This allows this mutex to support enabling assertions
*/
class SimpleMutex : public BaseMutex<true> {
public:
SimpleMutex(bool recursive = true, Rank rank = RankUnranked) :
BaseMutex<true>(recursive, rank) {}
};
///////////////////////////////////////////////////////////////////////////////
class SpinLock {
#ifdef DEBUG
Rank m_rank;
#endif
public:
SpinLock(Rank rank = RankUnranked)
#ifdef DEBUG
: m_rank(rank)
#endif
{
pthread_spin_init(&m_spinlock, 0);
}
~SpinLock() {
pthread_spin_destroy(&m_spinlock);
}
void lock() {
pushRank(m_rank);
pthread_spin_lock(&m_spinlock);
}
void unlock() {
popRank(m_rank);
pthread_spin_unlock(&m_spinlock);
}
pthread_spinlock_t &getRaw() { return m_spinlock;}
private:
SpinLock(const SpinLock &); // suppress
SpinLock &operator=(const SpinLock &); // suppress
pthread_spinlock_t m_spinlock;
};
///////////////////////////////////////////////////////////////////////////////
/**
* Read-write lock wrapper.
*/
class ReadWriteMutex {
#ifdef DEBUG
/*
* We have a track record of self-deadlocking on these, and our pthread
* implementation tends to do crazy things when a rwlock is double-wlocked,
* so check and assert early in debug builds.
*/
static const pthread_t InvalidThread = (pthread_t)0;
pthread_t m_writeOwner;
Rank m_rank;
#endif
void invalidateWriteOwner() {
#ifdef DEBUG
m_writeOwner = InvalidThread;
#endif
}
void recordWriteAcquire() {
#ifdef DEBUG
assert(m_writeOwner == InvalidThread);
m_writeOwner = pthread_self();
#endif
}
void assertNotWriteOwner() {
#ifdef DEBUG
assert(m_writeOwner != pthread_self());
#endif
}
void assertNotWriteOwned() {
#ifdef DEBUG
assert(m_writeOwner == InvalidThread);
#endif
}
public:
ReadWriteMutex(Rank rank = RankUnranked)
#ifdef DEBUG
: m_rank(rank)
#endif
{
invalidateWriteOwner();
pthread_rwlock_init(&m_rwlock, nullptr);
}
~ReadWriteMutex() {
assertNotWriteOwned();
pthread_rwlock_destroy(&m_rwlock);
}
void acquireRead() {
/*
* Atomically downgrading a write lock to a read lock is not part of the
* pthreads standard. See task #528421.
*/
assertNotWriteOwner();
pushRank(m_rank);
pthread_rwlock_rdlock(&m_rwlock);
/*
* Again, see task #528421.
*/
assertNotWriteOwned();
}
void acquireWrite() {
assertNotWriteOwner();
pushRank(m_rank);
pthread_rwlock_wrlock(&m_rwlock);
assertNotWriteOwned();
recordWriteAcquire();
}
bool attemptRead() { return !pthread_rwlock_tryrdlock(&m_rwlock); }
bool attemptWrite() { return !pthread_rwlock_trywrlock(&m_rwlock); }
void release() {
#ifdef DEBUG
popRank(m_rank);
if (m_writeOwner == pthread_self()) {
invalidateWriteOwner();
}
#endif
pthread_rwlock_unlock(&m_rwlock);
}
private:
ReadWriteMutex(const ReadWriteMutex &); // suppress
ReadWriteMutex &operator=(const ReadWriteMutex &); // suppress
pthread_rwlock_t m_rwlock;
};
/*
* A ranked wrapper around tbb::concurrent_hash_map.
*/
template<typename K, typename V, typename H=K, Rank R=RankUnranked>
class RankedCHM : public tbb::concurrent_hash_map<K, V, H> {
typedef tbb::concurrent_hash_map<K, V, H> RawCHM;
public:
class accessor : public RawCHM::accessor {
bool freed;
public:
accessor() : freed(false) { pushRank(R); }
~accessor() { if (!freed) popRank(R); }
void release() {
RawCHM::accessor::release();
popRank(R);
freed = true;
}
};
class const_accessor : public RawCHM::const_accessor {
bool freed;
public:
const_accessor() : freed(false) { pushRank(R); }
~const_accessor() { if (!freed) popRank(R); }
void release() {
RawCHM::const_accessor::release();
popRank(R);
freed = true;
}
};
bool find(const_accessor& a, const K& k) const { return RawCHM::find(a, k); }
bool find(accessor& a, const K& k) { return RawCHM::find(a, k); }
bool insert(accessor& a, const K& k) { return RawCHM::insert(a, k); }
bool insert(const_accessor& a, const K& k) { return RawCHM::insert(a, k); }
bool erase(accessor& a) { return RawCHM::erase(a); }
bool erase(const_accessor& a) { return RawCHM::erase(a); }
bool erase(const K& k) { return RawCHM::erase(k); }
};
///////////////////////////////////////////////////////////////////////////////
}
#endif // __MUTEX_H__