1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 /*
8  * Wrapper - Helper class for wrapper objects.
9  *
10  * This helps to construct a shared_ptr object which wraps access to an
11  * underlying handle. (The handle could be a pointer to some low-level type, a
12  * conventional C handle, an int ID, a GUID, etc.)
13  *
14  * Usage:
15  *   To obtain a FooPtr from a foo_handle_t, call
16  *     FooPtr Foo::wrap(foo_handle_t);
17  *
18  * To implement Foo using Wrapper, Foo needs to include this macro in its class
19  * definition:
20  *   CSF_DECLARE_WRAP(Foo, foo_handle_t);
21  * It also needs to include this in the cpp file, to provide the wrap()
22  * implementation and define the static Wrapper.
23  *   CSF_IMPLEMENT_WRAP(Foo, foo_handle_t);
24  * These are all declared in common/Wrapper.h - Foo.h needs to include this
25  * too.
26  * The client needs to declare Foo(foo_handle_t) as private, and provide a
27  * suitable implementation, as well as implementing wrappers for any other
28  * functions to be exposed.
29  * The client needs to implement ~Foo() to perform any cleanup as usual.
30  *
31  * wrap() will always return the same FooPtr for a given foo_handle_t, it will
32  * not construct additional objects if a suitable one already exists.
33  * changeHandle() is used in rare cases where the underlying handle is changed,
34  *                but the wrapper object is intended to remain.  This is the
35  *                case for the "fake" CC_DPCall generated on
36  *                CC_DPLine::CreateCall(), where the correct IDPCall* is
37  *                provided later.
38  * reset() is a cleanup step to wipe the handle map and allow memory to be
39  * reclaimed.
40  *
41  * Future enhancements:
42  * - For now, objects remain in the map forever.  Better would be to add a
43  *   releaseHandle() function which would allow the map to be emptied as
44  *   underlying handles expired.  While we can't force the client to give up
45  *   its shared_ptr<Foo> objects, we can remove our own copy, for instance on a
46  *   call ended event.
47  */
48 
49 #include <map>
50 #include "prlock.h"
51 #include "mozilla/Assertions.h"
52 
53 /*
54  * Wrapper has its own autolock class because the instances are declared
55  * statically and mozilla::Mutex will not work properly when instantiated
56  * in a static constructor.
57  */
58 
59 class LockNSPR {
60  public:
LockNSPR()61   LockNSPR() : lock_(nullptr) {
62     lock_ = PR_NewLock();
63     MOZ_ASSERT(lock_);
64   }
~LockNSPR()65   ~LockNSPR() { PR_DestroyLock(lock_); }
66 
Acquire()67   void Acquire() { PR_Lock(lock_); }
68 
Release()69   void Release() { PR_Unlock(lock_); }
70 
71  private:
72   PRLock* lock_;
73 };
74 
75 class AutoLockNSPR {
76  public:
AutoLockNSPR(LockNSPR & lock)77   explicit AutoLockNSPR(LockNSPR& lock) : lock_(lock) { lock_.Acquire(); }
~AutoLockNSPR()78   ~AutoLockNSPR() { lock_.Release(); }
79 
80  private:
81   LockNSPR& lock_;
82 };
83 
84 template <class T>
85 class Wrapper {
86  private:
87   typedef std::map<typename T::Handle, typename T::Ptr> HandleMapType;
88   HandleMapType handleMap;
89   LockNSPR handleMapMutex;
90 
91  public:
Wrapper()92   Wrapper() {}
93 
wrap(typename T::Handle handle)94   typename T::Ptr wrap(typename T::Handle handle) {
95     AutoLockNSPR lock(handleMapMutex);
96     typename HandleMapType::iterator it = handleMap.find(handle);
97     if (it != handleMap.end()) {
98       return it->second;
99     } else {
100       typename T::Ptr p(new T(handle));
101       handleMap[handle] = p;
102       return p;
103     }
104   }
105 
changeHandle(typename T::Handle oldHandle,typename T::Handle newHandle)106   bool changeHandle(typename T::Handle oldHandle,
107                     typename T::Handle newHandle) {
108     AutoLockNSPR lock(handleMapMutex);
109     typename HandleMapType::iterator it = handleMap.find(oldHandle);
110     if (it != handleMap.end()) {
111       typename T::Ptr p = it->second;
112       handleMap.erase(it);
113       handleMap[newHandle] = p;
114       return true;
115     } else {
116       return false;
117     }
118   }
119 
release(typename T::Handle handle)120   bool release(typename T::Handle handle) {
121     AutoLockNSPR lock(handleMapMutex);
122     typename HandleMapType::iterator it = handleMap.find(handle);
123     if (it != handleMap.end()) {
124       handleMap.erase(it);
125       return true;
126     } else {
127       return false;
128     }
129   }
130 
reset()131   void reset() {
132     AutoLockNSPR lock(handleMapMutex);
133     handleMap.clear();
134   }
135 };
136 
137 #define CSF_DECLARE_WRAP(classname, handletype)  \
138  public:                                         \
139   static classname##Ptr wrap(handletype handle); \
140   static void reset();                           \
141   static void release(handletype handle);        \
142                                                  \
143  private:                                        \
144   friend class Wrapper<classname>;               \
145   typedef classname##Ptr Ptr;                    \
146   typedef handletype Handle;                     \
147   static Wrapper<classname>& getWrapper() {      \
148     static Wrapper<classname> wrapper;           \
149     return wrapper;                              \
150   }
151 
152 #define CSF_IMPLEMENT_WRAP(classname, handletype)     \
153   classname##Ptr classname::wrap(handletype handle) { \
154     return getWrapper().wrap(handle);                 \
155   }                                                   \
156   void classname::reset() { getWrapper().reset(); }   \
157   void classname::release(handletype handle) { getWrapper().release(handle); }
158