class CAPABILITY("mutex") MutexLock : noncopyable
{
public:
MutexLock()
: holder_(0)
{
MCHECK(pthread_mutex_init(&mutex_, NULL));
}
~MutexLock()
{
assert(holder_ == 0);
MCHECK(pthread_mutex_destroy(&mutex_));
}
// must be called when locked, i.e. for assertion
bool isLockedByThisThread() const
{
return holder_ == CurrentThread::tid();
}
void assertLocked() const ASSERT_CAPABILITY(this)
{
assert(isLockedByThisThread());
}
// internal usage
void lock() ACQUIRE()
{
MCHECK(pthread_mutex_lock(&mutex_));
assignHolder();
}
void unlock() RELEASE()
{
unassignHolder();
MCHECK(pthread_mutex_unlock(&mutex_));
}
pthread_mutex_t* getPthreadMutex() /* non-const */
{
return &mutex_;
}
private:
friend class Condition;
class UnassignGuard : noncopyable
{
public:
explicit UnassignGuard(MutexLock& owner)
: owner_(owner)
{
owner_.unassignHolder();
}
~UnassignGuard()
{
owner_.assignHolder();
}
private:
MutexLock& owner_;
};
void unassignHolder()
{
holder_ = 0;
}
void assignHolder()
{
holder_ = CurrentThread::tid();
}
pthread_mutex_t mutex_;
pid_t holder_;
};
// Use as a stack variable, eg.
// int Foo::size() const
// {
// MutexLockGuard lock(mutex_);
// return data_.size();
// }
class SCOPED_CAPABILITY MutexLockGuard : noncopyable
{
public:
explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex)
: mutex_(mutex)
{
mutex_.lock();
}
~MutexLockGuard() RELEASE()
{
mutex_.unlock();
}
private:
MutexLock& mutex_;
};
Mutex.h头文件实现了一个采用RAII管理的锁。
其中MutexLock类就是实现了一个锁的基本操作,包含两个成员,mutex_就是内部锁,holder_记录拥有锁的线程,采用构造函数初始化锁,析构函数销毁锁,并提供了判断锁是否为本线程所持有的isLockedByThisThread接口,以及lock和unlock操作。getPthreadMutex是条件变量使用的接口,因为条件变量需要锁的指针。该类中最不容易理解的是私有类UnassignGuard的作用。先说MutexLockGuard,最后再说UnassignGuard。MutexLockGuard很简单,就是通过构造和析构来调用加解锁,将RAII原则贯彻到底,避免忘记释放锁。
最后说UnassignGuard,只有一个成员,即MutexLock的引用。该类只有构造函数和析构函数,其中构造函数将holder_赋值为0,析构函数将holder_赋值为当前线程,其实这个内部类,也是提供条件变量使用的,之后还会单独写一个关于条件变量的,但是这里先说一下。
在ThreadPool.cc中有这么一段代码,
MutexLockGuard lock(mutex_);
while (isFull() && running_)
{
notFull_.wait();
}
这时候MutexLockGuard会调用MutexLock的lock接口,lock接口会调用assignHolder将holder_赋值为当前线程,而notFull条件变量在调用wait接口代码如下:
void wait()
{
MutexLock::UnassignGuard ug(mutex_);
MCHECK(pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()));
}
可以看到wait里会先调用UnassignGuard的构造函数,之后pthread_cond_wait会释放mutex_,由于MutexLockGuard没有析构,所以无法通过MutexLockGuard析构函数调用unlock释放锁,在调用unlock时会将holder_赋值为0,所以UnassignGuard的构造函数相当于替代MutexLockGuard析构函数。再说UnassignGuard的析构函数,在pthread_cond_wait后释放了mutex_后,被其他线程获取该锁,之后释放,最终notFull条件变量被唤醒的时候,会自动加锁,可是这时候锁的holder_已经被其他线程修改了,所以这时候阻塞在notFull的线程获取的锁需要修改holder_为自己的线程ID,否则调用isLockedByThisThread就会失败,这个修改holder_的功能就是通过UnassignGuard的析构实现的。
上面大体上就是我现在的理解,可能有不对的地方,之后再修改吧。。。。