blob: bf887d23ce9216d50cb0458447daa1c6489fa985 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: jmaessen@google.com (Jan-Willem Maessen)
#ifndef PAGESPEED_KERNEL_BASE_CHECKING_THREAD_SYSTEM_H_
#define PAGESPEED_KERNEL_BASE_CHECKING_THREAD_SYSTEM_H_
#include "pagespeed/kernel/base/thread_system.h"
#include "pagespeed/kernel/base/atomic_bool.h"
#include "pagespeed/kernel/base/atomic_int32.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/thread_annotations.h"
namespace net_instaweb {
class Timer;
// A thread system whose mutex and condvar factories yield implementations that
// permit checking of lock invariants using DCheckLocked(). This can be wrapped
// around an unchecked implementation. This implementation checks invariants
// using CHECK (so does checking unconditionally). To check conditionally, do
// the wrapping depending upon the setting of NDEBUG. This is done by the
// Platform::CreateThreadSystem() factory by default, which is why the
// invariant checking method is called DCheckLock (Debug check lock) and not
// CheckLock.
class CheckingThreadSystem : public ThreadSystem {
private:
// C++ requires us to forward-declare this private class so that we can
// friend it in CheckingThreadSystem::Mutex.
// We otherwise try to keep it hidden from view.
class CheckingCondvar;
public:
// We also expose CheckingThreadSystem::Mutex, which wraps a
// CondvarCapableMutex to provide checked condvars and lock checking (these
// two must be done together, so we must wrap the mutex from which the condvar
// is created and use the wrapped mutex to create the condvar). This class
// can be used to wrap unchecked mutexes provided by other
// CheckingThreadSystems.
class LOCKABLE Mutex : public ThreadSystem::CondvarCapableMutex {
public:
explicit Mutex(ThreadSystem::CondvarCapableMutex* mutex) : mutex_(mutex) { }
virtual ~Mutex();
virtual bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true);
virtual void Lock() EXCLUSIVE_LOCK_FUNCTION();
virtual void Unlock() UNLOCK_FUNCTION();
// This implementation of DCheckLocked CHECK-fails if lock is not held.
virtual void DCheckLocked();
// This implementation of DCheckUnlocked CHECK-fails if lock is held.
virtual void DCheckUnlocked();
// The condvars provided perform lock checking for ....Wait operations.
virtual ThreadSystem::Condvar* NewCondvar();
private:
friend class CheckingCondvar;
void TakeLockControl();
void DropLockControl();
scoped_ptr<ThreadSystem::CondvarCapableMutex> mutex_;
AtomicBool locked_;
DISALLOW_COPY_AND_ASSIGN(Mutex);
};
// We also expose CheckingThreadSystem::RWLock, which wraps a
// RWLock to provide read/write capable locks. This class
// can be used to wrap unchecked mutexes provided by other
// CheckingThreadSystems.
class LOCKABLE RWLock : public ThreadSystem::RWLock {
public:
explicit RWLock(ThreadSystem::RWLock* lock) : lock_(lock) { }
virtual ~RWLock();
virtual bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true);
virtual void Lock() EXCLUSIVE_LOCK_FUNCTION();
virtual void Unlock() UNLOCK_FUNCTION();
virtual bool ReaderTryLock() SHARED_TRYLOCK_FUNCTION(true);
virtual void ReaderLock() SHARED_LOCK_FUNCTION();
virtual void ReaderUnlock() UNLOCK_FUNCTION();
// This implementation of DCheckLocked CHECK-fails if lock is not held.
virtual void DCheckLocked();
virtual void DCheckReaderLocked();
private:
void TakeLockControl();
void DropLockControl();
void TakeReaderLockControl();
void DropReaderLockControl();
scoped_ptr<ThreadSystem::RWLock> lock_;
AtomicInt32 locked_;
DISALLOW_COPY_AND_ASSIGN(RWLock);
};
explicit CheckingThreadSystem(ThreadSystem* thread_system)
: thread_system_(thread_system) { }
virtual ~CheckingThreadSystem();
virtual Mutex* NewMutex();
virtual RWLock* NewRWLock();
virtual Timer* NewTimer();
virtual ThreadId* GetThreadId() const {
return thread_system_->GetThreadId();
}
private:
friend class Mutex;
virtual ThreadImpl* NewThreadImpl(Thread* wrapper, ThreadFlags flags);
scoped_ptr<ThreadSystem> thread_system_;
DISALLOW_COPY_AND_ASSIGN(CheckingThreadSystem);
};
} // namespace net_instaweb
#endif // PAGESPEED_KERNEL_BASE_CHECKING_THREAD_SYSTEM_H_