pjs/xpcom/glue/nsAutoLock.h

459 строки
13 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
A stack-based lock object that makes using PRLock a bit more
convenient. It acquires the monitor when constructed, and releases
it when it goes out of scope.
For example,
class Foo {
private:
PRLock* mLock;
public:
Foo(void) {
mLock = PR_NewLock();
}
~Foo(void) {
PR_DestroyLock(mLock);
}
void ThreadSafeMethod(void) {
// we're don't hold the lock yet...
nsAutoLock lock(mLock);
// ...but now we do.
// we even can do wacky stuff like return from arbitrary places w/o
// worrying about forgetting to release the lock
if (some_weird_condition)
return;
// otherwise do some other stuff
}
void ThreadSafeBlockScope(void) {
// we're not in the lock here...
{
nsAutoLock lock(mLock);
// but we are now, at least until the block scope closes
}
// ...now we're not in the lock anymore
}
};
A similar stack-based locking object is available for PRMonitor. The
major difference is that the PRMonitor must be created and destroyed
via the static methods on nsAutoMonitor.
For example:
Foo::Foo() {
mMon = nsAutoMonitor::NewMonitor("FooMonitor");
}
nsresult Foo::MyMethod(...) {
nsAutoMonitor mon(mMon);
...
// go ahead and do deeply nested returns...
return NS_ERROR_FAILURE;
...
// or call Wait or Notify...
mon.Wait();
...
// cleanup is automatic
}
*/
#ifndef nsAutoLock_h__
#define nsAutoLock_h__
#include "nscore.h"
#include "prlock.h"
#include "prlog.h"
#include "mozilla/AutoRestore.h"
/**
* nsAutoLockBase
* This is the base class for the stack-based locking objects.
* Clients of derived classes need not play with this superclass.
**/
class NS_COM_GLUE NS_STACK_CLASS nsAutoLockBase {
friend class nsAutoUnlockBase;
protected:
nsAutoLockBase() {}
enum nsAutoLockType {eAutoLock, eAutoMonitor, eAutoCMonitor};
#ifdef DEBUG
nsAutoLockBase(void* addr, nsAutoLockType type);
~nsAutoLockBase();
void Show();
void Hide();
void* mAddr;
nsAutoLockBase* mDown;
nsAutoLockType mType;
#else
nsAutoLockBase(void* addr, nsAutoLockType type) {}
~nsAutoLockBase() {}
void Show() {}
void Hide() {}
#endif
};
/**
* nsAutoUnlockBase
* This is the base class for stack-based unlocking objects.
* It unlocks locking objects based on nsAutoLockBase.
**/
class NS_COM_GLUE NS_STACK_CLASS nsAutoUnlockBase {
protected:
nsAutoUnlockBase() {}
#ifdef DEBUG
nsAutoUnlockBase(void* addr);
~nsAutoUnlockBase();
nsAutoLockBase* mLock;
#else
nsAutoUnlockBase(void* addr) {}
~nsAutoUnlockBase() {}
#endif
};
/**
* nsAutoLock
* Stack-based locking object for PRLock.
**/
class NS_COM_GLUE NS_STACK_CLASS nsAutoLock : public nsAutoLockBase {
private:
PRLock* mLock;
PRBool mLocked;
MOZILLA_DECL_USE_GUARD_OBJECT_NOTIFIER
// Not meant to be implemented. This makes it a compiler error to
// construct or assign an nsAutoLock object incorrectly.
nsAutoLock(void) {}
nsAutoLock(nsAutoLock& /*aLock*/) {}
nsAutoLock& operator =(nsAutoLock& /*aLock*/) {
return *this;
}
// Not meant to be implemented. This makes it a compiler error to
// attempt to create an nsAutoLock object on the heap.
static void* operator new(size_t /*size*/) CPP_THROW_NEW {
return nsnull;
}
static void operator delete(void* /*memory*/) {}
public:
/**
* NewLock
* Allocates a new PRLock for use with nsAutoLock. name is
* not checked for uniqueness.
* @param name A name which can reference this lock
* @param lock A valid PRLock* that was created by nsAutoLock::NewLock()
* @returns nsnull if failure
* A valid PRLock* if successful, which must be destroyed
* by nsAutoLock::DestroyLock()
**/
static PRLock* NewLock(const char* name);
static void DestroyLock(PRLock* lock);
/**
* Constructor
* The constructor aquires the given lock. The destructor
* releases the lock.
*
* @param aLock A valid PRLock* returned from the NSPR's
* PR_NewLock() function.
**/
nsAutoLock(PRLock* aLock MOZILLA_GUARD_OBJECT_NOTIFIER_PARAM)
: nsAutoLockBase(aLock, eAutoLock),
mLock(aLock),
mLocked(PR_TRUE) {
MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
PR_ASSERT(mLock);
// This will assert deep in the bowels of NSPR if you attempt
// to re-enter the lock.
PR_Lock(mLock);
}
~nsAutoLock(void) {
if (mLocked)
PR_Unlock(mLock);
}
/**
* lock
* Client may call this to reaquire the given lock. Take special
* note that attempting to aquire a locked lock will hang or crash.
**/
void lock() {
Show();
PR_ASSERT(!mLocked);
PR_Lock(mLock);
mLocked = PR_TRUE;
}
/**
* unlock
* Client may call this to release the given lock. Take special
* note unlocking an unlocked lock has undefined results.
**/
void unlock() {
PR_ASSERT(mLocked);
PR_Unlock(mLock);
mLocked = PR_FALSE;
Hide();
}
};
class NS_STACK_CLASS nsAutoUnlock : private nsAutoUnlockBase
{
private:
PRLock *mLock;
MOZILLA_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
nsAutoUnlock(PRLock *lock MOZILLA_GUARD_OBJECT_NOTIFIER_PARAM) :
nsAutoUnlockBase(lock),
mLock(lock)
{
MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
PR_Unlock(mLock);
}
~nsAutoUnlock() {
PR_Lock(mLock);
}
};
#include "prcmon.h"
#include "nsError.h"
#include "nsDebug.h"
class NS_COM_GLUE NS_STACK_CLASS nsAutoMonitor : public nsAutoLockBase {
public:
/**
* NewMonitor
* Allocates a new PRMonitor for use with nsAutoMonitor.
* @param name A (unique /be?) name which can reference this monitor
* @returns nsnull if failure
* A valid PRMonitor* is successful while must be destroyed
* by nsAutoMonitor::DestroyMonitor()
**/
static PRMonitor* NewMonitor(const char* name);
static void DestroyMonitor(PRMonitor* mon);
/**
* Constructor
* The constructor locks the given monitor. During destruction
* the monitor will be unlocked.
*
* @param mon A valid PRMonitor* returned from
* nsAutoMonitor::NewMonitor().
**/
nsAutoMonitor(PRMonitor* mon MOZILLA_GUARD_OBJECT_NOTIFIER_PARAM)
: nsAutoLockBase((void*)mon, eAutoMonitor),
mMonitor(mon), mLockCount(0)
{
MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
NS_ASSERTION(mMonitor, "null monitor");
if (mMonitor) {
PR_EnterMonitor(mMonitor);
mLockCount = 1;
}
}
~nsAutoMonitor() {
NS_ASSERTION(mMonitor, "null monitor");
if (mMonitor && mLockCount) {
#ifdef DEBUG
PRStatus status =
#endif
PR_ExitMonitor(mMonitor);
NS_ASSERTION(status == PR_SUCCESS, "PR_ExitMonitor failed");
}
}
/**
* Enter
* Client may call this to reenter the given monitor.
* @see prmon.h
**/
void Enter();
/**
* Exit
* Client may call this to exit the given monitor.
* @see prmon.h
**/
void Exit();
/**
* Wait
* @see prmon.h
**/
nsresult Wait(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT) {
return PR_Wait(mMonitor, interval) == PR_SUCCESS
? NS_OK : NS_ERROR_FAILURE;
}
/**
* Notify
* @see prmon.h
**/
nsresult Notify() {
return PR_Notify(mMonitor) == PR_SUCCESS
? NS_OK : NS_ERROR_FAILURE;
}
/**
* NotifyAll
* @see prmon.h
**/
nsresult NotifyAll() {
return PR_NotifyAll(mMonitor) == PR_SUCCESS
? NS_OK : NS_ERROR_FAILURE;
}
private:
PRMonitor* mMonitor;
PRInt32 mLockCount;
MOZILLA_DECL_USE_GUARD_OBJECT_NOTIFIER
// Not meant to be implemented. This makes it a compiler error to
// construct or assign an nsAutoLock object incorrectly.
nsAutoMonitor(void) {}
nsAutoMonitor(nsAutoMonitor& /*aMon*/) {}
nsAutoMonitor& operator =(nsAutoMonitor& /*aMon*/) {
return *this;
}
// Not meant to be implemented. This makes it a compiler error to
// attempt to create an nsAutoLock object on the heap.
static void* operator new(size_t /*size*/) CPP_THROW_NEW {
return nsnull;
}
static void operator delete(void* /*memory*/) {}
};
////////////////////////////////////////////////////////////////////////////////
// Once again, this time with a cache...
// (Using this avoids the need to allocate a PRMonitor, which may be useful when
// a large number of objects of the same class need associated monitors.)
#include "prcmon.h"
#include "nsError.h"
class NS_COM_GLUE NS_STACK_CLASS nsAutoCMonitor : public nsAutoLockBase {
public:
nsAutoCMonitor(void* lockObject MOZILLA_GUARD_OBJECT_NOTIFIER_PARAM)
: nsAutoLockBase(lockObject, eAutoCMonitor),
mLockObject(lockObject), mLockCount(0)
{
MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
NS_ASSERTION(lockObject, "null lock object");
PR_CEnterMonitor(mLockObject);
mLockCount = 1;
}
~nsAutoCMonitor() {
if (mLockCount) {
#ifdef DEBUG
PRStatus status =
#endif
PR_CExitMonitor(mLockObject);
NS_ASSERTION(status == PR_SUCCESS, "PR_CExitMonitor failed");
}
}
void Enter();
void Exit();
nsresult Wait(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT) {
return PR_CWait(mLockObject, interval) == PR_SUCCESS
? NS_OK : NS_ERROR_FAILURE;
}
nsresult Notify() {
return PR_CNotify(mLockObject) == PR_SUCCESS
? NS_OK : NS_ERROR_FAILURE;
}
nsresult NotifyAll() {
return PR_CNotifyAll(mLockObject) == PR_SUCCESS
? NS_OK : NS_ERROR_FAILURE;
}
private:
void* mLockObject;
PRInt32 mLockCount;
MOZILLA_DECL_USE_GUARD_OBJECT_NOTIFIER
// Not meant to be implemented. This makes it a compiler error to
// construct or assign an nsAutoLock object incorrectly.
nsAutoCMonitor(void) {}
nsAutoCMonitor(nsAutoCMonitor& /*aMon*/) {}
nsAutoCMonitor& operator =(nsAutoCMonitor& /*aMon*/) {
return *this;
}
// Not meant to be implemented. This makes it a compiler error to
// attempt to create an nsAutoLock object on the heap.
static void* operator new(size_t /*size*/) CPP_THROW_NEW {
return nsnull;
}
static void operator delete(void* /*memory*/) {}
};
#endif // nsAutoLock_h__