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