michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #pragma once michael@0: michael@0: /* michael@0: * Wrapper - Helper class for wrapper objects. michael@0: * michael@0: * This helps to construct a shared_ptr object which wraps access to an underlying handle. michael@0: * (The handle could be a pointer to some low-level type, a conventional C handle, an int ID, a GUID, etc.) michael@0: * michael@0: * Usage: michael@0: * To obtain a FooPtr from a foo_handle_t, call FooPtr Foo::wrap(foo_handle_t); michael@0: * michael@0: * To implement Foo using Wrapper, Foo needs to include this macro in its class definition: michael@0: * CSF_DECLARE_WRAP(Foo, foo_handle_t); michael@0: * It also needs to include this in the cpp file, to provide the wrap() implementation and define the static Wrapper. michael@0: * CSF_IMPLEMENT_WRAP(Foo, foo_handle_t); michael@0: * These are all declared in common/Wrapper.h - Foo.h needs to include this too. michael@0: * The client needs to declare Foo(foo_handle_t) as private, and provide a suitable implementation, as well as michael@0: * implementing wrappers for any other functions to be exposed. michael@0: * The client needs to implement ~Foo() to perform any cleanup as usual. michael@0: * michael@0: * wrap() will always return the same FooPtr for a given foo_handle_t, it will not construct additional objects michael@0: * if a suitable one already exists. michael@0: * changeHandle() is used in rare cases where the underlying handle is changed, but the wrapper object is intended michael@0: * to remain. This is the case for the "fake" CC_DPCall generated on CC_DPLine::CreateCall(), where michael@0: * the correct IDPCall* is provided later. michael@0: * reset() is a cleanup step to wipe the handle map and allow memory to be reclaimed. michael@0: * michael@0: * Future enhancements: michael@0: * - For now, objects remain in the map forever. Better would be to add a releaseHandle() function which would michael@0: * allow the map to be emptied as underlying handles expired. While we can't force the client to give up its michael@0: * shared_ptr objects, we can remove our own copy, for instance on a call ended event. michael@0: */ michael@0: michael@0: #include michael@0: #include "prlock.h" michael@0: #include "mozilla/Assertions.h" michael@0: michael@0: /* michael@0: * Wrapper has its own autolock class because the instances are declared michael@0: * statically and mozilla::Mutex will not work properly when instantiated michael@0: * in a static constructor. michael@0: */ michael@0: michael@0: class LockNSPR { michael@0: public: michael@0: LockNSPR() : lock_(nullptr) { michael@0: lock_ = PR_NewLock(); michael@0: MOZ_ASSERT(lock_); michael@0: } michael@0: ~LockNSPR() { michael@0: PR_DestroyLock(lock_); michael@0: } michael@0: michael@0: void Acquire() { michael@0: PR_Lock(lock_); michael@0: } michael@0: michael@0: void Release() { michael@0: PR_Unlock(lock_); michael@0: } michael@0: michael@0: private: michael@0: PRLock *lock_; michael@0: }; michael@0: michael@0: class AutoLockNSPR { michael@0: public: michael@0: AutoLockNSPR(LockNSPR& lock) : lock_(lock) { michael@0: lock_.Acquire(); michael@0: } michael@0: ~AutoLockNSPR() { michael@0: lock_.Release(); michael@0: } michael@0: michael@0: private: michael@0: LockNSPR& lock_; michael@0: }; michael@0: michael@0: template michael@0: class Wrapper michael@0: { michael@0: private: michael@0: typedef std::map HandleMapType; michael@0: HandleMapType handleMap; michael@0: LockNSPR handleMapMutex; michael@0: michael@0: public: michael@0: Wrapper() {} michael@0: michael@0: typename T::Ptr wrap(typename T::Handle handle) michael@0: { michael@0: AutoLockNSPR lock(handleMapMutex); michael@0: typename HandleMapType::iterator it = handleMap.find(handle); michael@0: if(it != handleMap.end()) michael@0: { michael@0: return it->second; michael@0: } michael@0: else michael@0: { michael@0: typename T::Ptr p(new T(handle)); michael@0: handleMap[handle] = p; michael@0: return p; michael@0: } michael@0: } michael@0: michael@0: bool changeHandle(typename T::Handle oldHandle, typename T::Handle newHandle) michael@0: { michael@0: AutoLockNSPR lock(handleMapMutex); michael@0: typename HandleMapType::iterator it = handleMap.find(oldHandle); michael@0: if(it != handleMap.end()) michael@0: { michael@0: typename T::Ptr p = it->second; michael@0: handleMap.erase(it); michael@0: handleMap[newHandle] = p; michael@0: return true; michael@0: } michael@0: else michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool release(typename T::Handle handle) michael@0: { michael@0: AutoLockNSPR lock(handleMapMutex); michael@0: typename HandleMapType::iterator it = handleMap.find(handle); michael@0: if(it != handleMap.end()) michael@0: { michael@0: handleMap.erase(it); michael@0: return true; michael@0: } michael@0: else michael@0: { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: void reset() michael@0: { michael@0: AutoLockNSPR lock(handleMapMutex); michael@0: handleMap.clear(); michael@0: } michael@0: }; michael@0: michael@0: #define CSF_DECLARE_WRAP(classname, handletype) \ michael@0: public: \ michael@0: static classname ## Ptr wrap(handletype handle); \ michael@0: static void reset(); \ michael@0: static void release(handletype handle); \ michael@0: private: \ michael@0: friend class Wrapper; \ michael@0: typedef classname ## Ptr Ptr; \ michael@0: typedef handletype Handle; \ michael@0: static Wrapper& getWrapper() { \ michael@0: static Wrapper wrapper; \ michael@0: return wrapper; \ michael@0: } michael@0: michael@0: #define CSF_IMPLEMENT_WRAP(classname, handletype) \ michael@0: classname ## Ptr classname::wrap(handletype handle) \ michael@0: { \ michael@0: return getWrapper().wrap(handle); \ michael@0: } \ michael@0: void classname::reset() \ michael@0: { \ michael@0: getWrapper().reset(); \ michael@0: } \ michael@0: void classname::release(handletype handle) \ michael@0: { \ michael@0: getWrapper().release(handle); \ michael@0: }