1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef mozilla_mscom_Ptr_h
8 #define mozilla_mscom_Ptr_h
9
10 #include "mozilla/Assertions.h"
11 #include "mozilla/DebugOnly.h"
12 #include "mozilla/mscom/EnsureMTA.h"
13 #include "mozilla/SystemGroup.h"
14 #include "mozilla/UniquePtr.h"
15 #include "nsError.h"
16 #include "nsThreadUtils.h"
17 #include "nsXULAppAPI.h"
18
19 /**
20 * The glue code in mozilla::mscom often needs to pass around interface pointers
21 * belonging to a different apartment from the current one. We must not touch
22 * the reference counts of those objects on the wrong apartment. By using these
23 * UniquePtr specializations, we may ensure that the reference counts are always
24 * handled correctly.
25 */
26
27 namespace mozilla {
28 namespace mscom {
29
30 namespace detail {
31
32 template <typename T>
33 struct MainThreadRelease {
operatorMainThreadRelease34 void operator()(T* aPtr) {
35 if (!aPtr) {
36 return;
37 }
38 if (NS_IsMainThread()) {
39 aPtr->Release();
40 return;
41 }
42 DebugOnly<nsresult> rv = SystemGroup::Dispatch(
43 TaskCategory::Other,
44 NewNonOwningRunnableMethod("mscom::MainThreadRelease", aPtr,
45 &T::Release));
46 MOZ_ASSERT(NS_SUCCEEDED(rv));
47 }
48 };
49
50 template <typename T>
51 struct MTADelete {
operatorMTADelete52 void operator()(T* aPtr) {
53 if (!aPtr) {
54 return;
55 }
56
57 EnsureMTA::AsyncOperation([aPtr]() -> void { delete aPtr; });
58 }
59 };
60
61 template <typename T>
62 struct MTARelease {
operatorMTARelease63 void operator()(T* aPtr) {
64 if (!aPtr) {
65 return;
66 }
67
68 // Static analysis doesn't recognize that, even though aPtr escapes the
69 // current scope, we are in effect moving our strong ref into the lambda.
70 void* ptr = aPtr;
71 EnsureMTA::AsyncOperation(
72 [ptr]() -> void { reinterpret_cast<T*>(ptr)->Release(); });
73 }
74 };
75
76 template <typename T>
77 struct MTAReleaseInChildProcess {
operatorMTAReleaseInChildProcess78 void operator()(T* aPtr) {
79 if (!aPtr) {
80 return;
81 }
82
83 if (XRE_IsParentProcess()) {
84 MOZ_ASSERT(NS_IsMainThread());
85 aPtr->Release();
86 return;
87 }
88
89 // Static analysis doesn't recognize that, even though aPtr escapes the
90 // current scope, we are in effect moving our strong ref into the lambda.
91 void* ptr = aPtr;
92 EnsureMTA::AsyncOperation(
93 [ptr]() -> void { reinterpret_cast<T*>(ptr)->Release(); });
94 }
95 };
96
97 struct InterceptorTargetDeleter {
operatorInterceptorTargetDeleter98 void operator()(IUnknown* aPtr) {
99 // We intentionally do not touch the refcounts of interceptor targets!
100 }
101 };
102
103 struct PreservedStreamDeleter {
operatorPreservedStreamDeleter104 void operator()(IStream* aPtr) {
105 if (!aPtr) {
106 return;
107 }
108
109 // Static analysis doesn't recognize that, even though aPtr escapes the
110 // current scope, we are in effect moving our strong ref into the lambda.
111 void* ptr = aPtr;
112 auto cleanup = [ptr]() -> void {
113 DebugOnly<HRESULT> hr =
114 ::CoReleaseMarshalData(reinterpret_cast<LPSTREAM>(ptr));
115 MOZ_ASSERT(SUCCEEDED(hr));
116 reinterpret_cast<LPSTREAM>(ptr)->Release();
117 };
118
119 if (XRE_IsParentProcess()) {
120 MOZ_ASSERT(NS_IsMainThread());
121 cleanup();
122 return;
123 }
124
125 EnsureMTA::AsyncOperation(cleanup);
126 }
127 };
128
129 } // namespace detail
130
131 template <typename T>
132 using STAUniquePtr = mozilla::UniquePtr<T, detail::MainThreadRelease<T>>;
133
134 template <typename T>
135 using MTAUniquePtr = mozilla::UniquePtr<T, detail::MTARelease<T>>;
136
137 template <typename T>
138 using MTADeletePtr = mozilla::UniquePtr<T, detail::MTADelete<T>>;
139
140 template <typename T>
141 using ProxyUniquePtr =
142 mozilla::UniquePtr<T, detail::MTAReleaseInChildProcess<T>>;
143
144 template <typename T>
145 using InterceptorTargetPtr =
146 mozilla::UniquePtr<T, detail::InterceptorTargetDeleter>;
147
148 using PreservedStreamPtr =
149 mozilla::UniquePtr<IStream, detail::PreservedStreamDeleter>;
150
151 namespace detail {
152
153 // We don't have direct access to UniquePtr's storage, so we use mPtrStorage
154 // to receive the pointer and then set the target inside the destructor.
155 template <typename T, typename Deleter>
156 class UniquePtrGetterAddRefs {
157 public:
UniquePtrGetterAddRefs(UniquePtr<T,Deleter> & aSmartPtr)158 explicit UniquePtrGetterAddRefs(UniquePtr<T, Deleter>& aSmartPtr)
159 : mTargetSmartPtr(aSmartPtr), mPtrStorage(nullptr) {}
160
~UniquePtrGetterAddRefs()161 ~UniquePtrGetterAddRefs() { mTargetSmartPtr.reset(mPtrStorage); }
162
163 operator void**() { return reinterpret_cast<void**>(&mPtrStorage); }
164
165 operator T**() { return &mPtrStorage; }
166
167 T*& operator*() { return mPtrStorage; }
168
169 private:
170 UniquePtr<T, Deleter>& mTargetSmartPtr;
171 T* mPtrStorage;
172 };
173
174 } // namespace detail
175
176 template <typename T>
ToSTAUniquePtr(RefPtr<T> && aRefPtr)177 inline STAUniquePtr<T> ToSTAUniquePtr(RefPtr<T>&& aRefPtr) {
178 return STAUniquePtr<T>(aRefPtr.forget().take());
179 }
180
181 template <typename T>
ToSTAUniquePtr(const RefPtr<T> & aRefPtr)182 inline STAUniquePtr<T> ToSTAUniquePtr(const RefPtr<T>& aRefPtr) {
183 MOZ_ASSERT(NS_IsMainThread());
184 return STAUniquePtr<T>(do_AddRef(aRefPtr).take());
185 }
186
187 template <typename T>
ToSTAUniquePtr(T * aRawPtr)188 inline STAUniquePtr<T> ToSTAUniquePtr(T* aRawPtr) {
189 MOZ_ASSERT(NS_IsMainThread());
190 if (aRawPtr) {
191 aRawPtr->AddRef();
192 }
193 return STAUniquePtr<T>(aRawPtr);
194 }
195
196 template <typename T, typename U>
ToSTAUniquePtr(const InterceptorTargetPtr<U> & aTarget)197 inline STAUniquePtr<T> ToSTAUniquePtr(const InterceptorTargetPtr<U>& aTarget) {
198 MOZ_ASSERT(NS_IsMainThread());
199 RefPtr<T> newRef(static_cast<T*>(aTarget.get()));
200 return ToSTAUniquePtr(Move(newRef));
201 }
202
203 template <typename T>
ToMTAUniquePtr(RefPtr<T> && aRefPtr)204 inline MTAUniquePtr<T> ToMTAUniquePtr(RefPtr<T>&& aRefPtr) {
205 return MTAUniquePtr<T>(aRefPtr.forget().take());
206 }
207
208 template <typename T>
ToMTAUniquePtr(const RefPtr<T> & aRefPtr)209 inline MTAUniquePtr<T> ToMTAUniquePtr(const RefPtr<T>& aRefPtr) {
210 MOZ_ASSERT(IsCurrentThreadMTA());
211 return MTAUniquePtr<T>(do_AddRef(aRefPtr).take());
212 }
213
214 template <typename T>
ToMTAUniquePtr(T * aRawPtr)215 inline MTAUniquePtr<T> ToMTAUniquePtr(T* aRawPtr) {
216 MOZ_ASSERT(IsCurrentThreadMTA());
217 if (aRawPtr) {
218 aRawPtr->AddRef();
219 }
220 return MTAUniquePtr<T>(aRawPtr);
221 }
222
223 template <typename T>
ToProxyUniquePtr(RefPtr<T> && aRefPtr)224 inline ProxyUniquePtr<T> ToProxyUniquePtr(RefPtr<T>&& aRefPtr) {
225 return ProxyUniquePtr<T>(aRefPtr.forget().take());
226 }
227
228 template <typename T>
ToProxyUniquePtr(const RefPtr<T> & aRefPtr)229 inline ProxyUniquePtr<T> ToProxyUniquePtr(const RefPtr<T>& aRefPtr) {
230 MOZ_ASSERT(IsProxy(aRefPtr));
231 MOZ_ASSERT((XRE_IsParentProcess() && NS_IsMainThread()) ||
232 (XRE_IsContentProcess() && IsCurrentThreadMTA()));
233
234 return ProxyUniquePtr<T>(do_AddRef(aRefPtr).take());
235 }
236
237 template <typename T>
ToProxyUniquePtr(T * aRawPtr)238 inline ProxyUniquePtr<T> ToProxyUniquePtr(T* aRawPtr) {
239 MOZ_ASSERT(IsProxy(aRawPtr));
240 MOZ_ASSERT((XRE_IsParentProcess() && NS_IsMainThread()) ||
241 (XRE_IsContentProcess() && IsCurrentThreadMTA()));
242
243 if (aRawPtr) {
244 aRawPtr->AddRef();
245 }
246 return ProxyUniquePtr<T>(aRawPtr);
247 }
248
249 template <typename T, typename Deleter>
ToInterceptorTargetPtr(const UniquePtr<T,Deleter> & aTargetPtr)250 inline InterceptorTargetPtr<T> ToInterceptorTargetPtr(
251 const UniquePtr<T, Deleter>& aTargetPtr) {
252 return InterceptorTargetPtr<T>(aTargetPtr.get());
253 }
254
ToPreservedStreamPtr(RefPtr<IStream> && aStream)255 inline PreservedStreamPtr ToPreservedStreamPtr(RefPtr<IStream>&& aStream) {
256 return PreservedStreamPtr(aStream.forget().take());
257 }
258
ToPreservedStreamPtr(already_AddRefed<IStream> & aStream)259 inline PreservedStreamPtr ToPreservedStreamPtr(
260 already_AddRefed<IStream>& aStream) {
261 return PreservedStreamPtr(aStream.take());
262 }
263
264 template <typename T, typename Deleter>
getter_AddRefs(UniquePtr<T,Deleter> & aSmartPtr)265 inline detail::UniquePtrGetterAddRefs<T, Deleter> getter_AddRefs(
266 UniquePtr<T, Deleter>& aSmartPtr) {
267 return detail::UniquePtrGetterAddRefs<T, Deleter>(aSmartPtr);
268 }
269
270 } // namespace mscom
271 } // namespace mozilla
272
273 // This block makes it possible for these smart pointers to be correctly
274 // applied in NewRunnableMethod and friends
275 namespace detail {
276
277 template <typename T>
278 struct SmartPointerStorageClass<mozilla::mscom::STAUniquePtr<T>> {
279 typedef StoreCopyPassByRRef<mozilla::mscom::STAUniquePtr<T>> Type;
280 };
281
282 template <typename T>
283 struct SmartPointerStorageClass<mozilla::mscom::MTAUniquePtr<T>> {
284 typedef StoreCopyPassByRRef<mozilla::mscom::MTAUniquePtr<T>> Type;
285 };
286
287 template <typename T>
288 struct SmartPointerStorageClass<mozilla::mscom::ProxyUniquePtr<T>> {
289 typedef StoreCopyPassByRRef<mozilla::mscom::ProxyUniquePtr<T>> Type;
290 };
291
292 template <typename T>
293 struct SmartPointerStorageClass<mozilla::mscom::InterceptorTargetPtr<T>> {
294 typedef StoreCopyPassByRRef<mozilla::mscom::InterceptorTargetPtr<T>> Type;
295 };
296
297 template <>
298 struct SmartPointerStorageClass<mozilla::mscom::PreservedStreamPtr> {
299 typedef StoreCopyPassByRRef<mozilla::mscom::PreservedStreamPtr> Type;
300 };
301
302 } // namespace detail
303
304 #endif // mozilla_mscom_Ptr_h
305