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