/* -*- 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" /** * 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 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 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 nsAutoLock : public nsAutoLockBase { private: PRLock* mLock; PRBool mLocked; // 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) : nsAutoLockBase(aLock, eAutoLock), mLock(aLock), mLocked(PR_TRUE) { 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(); } }; #include "prcmon.h" #include "nsError.h" #include "nsDebug.h" class NS_COM 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) : nsAutoLockBase((void*)mon, eAutoMonitor), mMonitor(mon), mLockCount(0) { 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; // 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 nsAutoCMonitor : public nsAutoLockBase { public: nsAutoCMonitor(void* lockObject) : nsAutoLockBase(lockObject, eAutoCMonitor), mLockObject(lockObject), mLockCount(0) { 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; // 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__