/* -*- 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 #include #include /** * 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 { protected: nsAutoLockBase() {} enum nsAutoLockType {eAutoLock, eAutoMonitor, eAutoCMonitor}; nsAutoLockBase(void* addr, nsAutoLockType type) {} ~nsAutoLockBase() {} }; /** * nsAutoLock * Stack-based locking object for PRLock. **/ class NS_COM nsAutoLock : public nsAutoLockBase { private: /** The IPRT fast mutex. */ RTSEMFASTMUTEX m_hMtx; bool m_fLocked; // Not meant to be implemented. This makes it a compiler error to // construct or assign an nsAutoLock object incorrectly. nsAutoLock(void); nsAutoLock(const nsAutoLock& /*aLock*/); nsAutoLock& operator =(const nsAutoLock& /*aLock*/); // 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; static void operator delete(void* /*memory*/); public: /** * Constructor * The constructor aquires the given lock. The destructor * releases the lock. * * @param hMtx A valid IPRT fast mutex. **/ nsAutoLock(RTSEMFASTMUTEX hMtx) : nsAutoLockBase(hMtx, eAutoLock), m_hMtx(hMtx), m_fLocked(true) { Assert(hMtx != NIL_RTSEMFASTMUTEX); RTSemFastMutexRequest(m_hMtx); } ~nsAutoLock(void) { if (m_fLocked) RTSemFastMutexRelease(m_hMtx); } /** * 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() { Assert(!m_fLocked); Assert(m_hMtx != NIL_RTSEMFASTMUTEX); RTSemFastMutexRequest(m_hMtx); m_fLocked = true; } /** * unlock * Client may call this to release the given lock. Take special * note unlocking an unlocked lock has undefined results. **/ void unlock() { Assert(m_fLocked); Assert(m_hMtx != NIL_RTSEMFASTMUTEX); RTSemFastMutexRelease(m_hMtx); m_fLocked = false; } }; #include "prmon.h" #include "nsError.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) { AssertMsg(mMonitor, ("null monitor")); if (mMonitor) { PR_EnterMonitor(mMonitor); mLockCount = 1; } } ~nsAutoMonitor() { AssertMsg(mMonitor, ("null monitor")); if (mMonitor && mLockCount) { PR_ExitMonitor(mMonitor); } } /** * 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(RTMSINTERVAL msTimeout = RT_INDEFINITE_WAIT) { return PR_Wait(mMonitor, msTimeout) == 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(const nsAutoMonitor& /*aMon*/); nsAutoMonitor& operator =(const nsAutoMonitor& /*aMon*/); // 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; static void operator delete(void* /*memory*/); }; /** * Cut down version of the nsAutoMonitor where the passed monitor can be NULL. * Used in exactly one place because the regular nsAutoMonitor would assert in that place * during shutdown (see nsComponentManager::NS_GetServiceManager for an explanation while * the assertion is nothing to orry about actually). */ class NS_COM nsAutoMonitorCanBeNull : public nsAutoLockBase { public: /** * Constructor * The constructor locks the given monitor. During destruction * the monitor will be unlocked. * * @param mon A valid PRMonitor* returned from * nsAutoMonitor::NewMonitor(). **/ nsAutoMonitorCanBeNull(PRMonitor* mon) : nsAutoLockBase((void*)mon, eAutoMonitor), mMonitor(mon), mLockCount(0) { if (mMonitor) { PR_EnterMonitor(mMonitor); mLockCount = 1; } } ~nsAutoMonitorCanBeNull() { if (mMonitor && mLockCount) { PR_ExitMonitor(mMonitor); } } private: PRMonitor* mMonitor; PRInt32 mLockCount; // Not meant to be implemented. This makes it a compiler error to // construct or assign an nsAutoLock object incorrectly. nsAutoMonitorCanBeNull(void); nsAutoMonitorCanBeNull(const nsAutoMonitorCanBeNull& /*aMon*/); nsAutoMonitorCanBeNull& operator =(const nsAutoMonitorCanBeNull& /*aMon*/); // 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; static void operator delete(void* /*memory*/); }; #endif // nsAutoLock_h__