Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(647)

Side by Side Diff: ppapi/shared_impl/proxy_lock.h

Issue 19492014: PPAPI: Purposely leak ProxyLock, fix shutdown race (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: test cleanup Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 5 #ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_
6 #define PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 6 #define PPAPI_SHARED_IMPL_PROXY_LOCK_H_
7 7
8 #include "base/basictypes.h" 8 #include "base/basictypes.h"
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
11 #include "base/threading/thread_checker.h" 11 #include "base/threading/thread_checker.h"
12 12
13 #include "ppapi/shared_impl/ppapi_shared_export.h" 13 #include "ppapi/shared_impl/ppapi_shared_export.h"
14 14
15 namespace base { 15 namespace base {
16 class Lock; 16 class Lock;
17 } 17 }
18 18
19 namespace content {
20 class HostGlobals;
21 }
22
19 namespace ppapi { 23 namespace ppapi {
20 24
21 // This is the one lock to rule them all for the ppapi proxy. All PPB interface 25 // This is the one lock to rule them all for the ppapi proxy. All PPB interface
22 // functions that need to be synchronized should lock this lock on entry. This 26 // functions that need to be synchronized should lock this lock on entry. This
23 // is normally accomplished by using an appropriate Enter RAII object at the 27 // is normally accomplished by using an appropriate Enter RAII object at the
24 // beginning of each thunk function. 28 // beginning of each thunk function.
25 // 29 //
26 // TODO(dmichael): If this turns out to be too slow and contentious, we'll want 30 // TODO(dmichael): If this turns out to be too slow and contentious, we'll want
27 // to use multiple locks. E.g., one for the var tracker, one for the resource 31 // to use multiple locks. E.g., one for the var tracker, one for the resource
28 // tracker, etc. 32 // tracker, etc.
29 class PPAPI_SHARED_EXPORT ProxyLock { 33 class PPAPI_SHARED_EXPORT ProxyLock {
30 public: 34 public:
35 // Return the globally unique ProxyLock. Normally, you should not access this
bbudge 2013/07/29 21:02:18 s/globally unique/global ?
dmichael (off chromium) 2013/07/29 21:24:03 Done.
36 // directly but instead use ProxyAutoLock or ProxyAutoUnlock. But sometimes
37 // you need access to the ProxyLock for e.g. a condition variable.
bbudge 2013/07/29 21:02:18 s/ for e.g./e.g. to create
dmichael (off chromium) 2013/07/29 21:24:03 Done.
38 static base::Lock* Get();
39
31 // Acquire the proxy lock. If it is currently held by another thread, block 40 // Acquire the proxy lock. If it is currently held by another thread, block
32 // until it is available. If the lock has not been set using the 'Set' method, 41 // until it is available. If the lock has not been set using the 'Set' method,
33 // this operation does nothing. That is the normal case for the host side; 42 // this operation does nothing. That is the normal case for the host side;
34 // see PluginResourceTracker for where the lock gets set for the out-of- 43 // see PluginResourceTracker for where the lock gets set for the out-of-
35 // process plugin case. 44 // process plugin case.
36 static void Acquire(); 45 static void Acquire();
37 // Relinquish the proxy lock. If the lock has not been set, this does nothing. 46 // Relinquish the proxy lock. If the lock has not been set, this does nothing.
38 static void Release(); 47 static void Release();
39 48
40 // Assert that the lock is owned by the current thread (in the plugin 49 // Assert that the lock is owned by the current thread (in the plugin
41 // process). Does nothing when running in-process (or in the host process). 50 // process). Does nothing when running in-process (or in the host process).
42 static void AssertAcquired(); 51 static void AssertAcquired();
43 52
53 // We have some unit tests where one thread pretends to be the host and one
54 // pretends to be the plugin. This allows the lock to do nothing on only one
55 // thread to support these tests. See TwoWayTest for more information.
56 static void DisableLockingOnThreadForTest();
57
58 // Locking will be on by default, but unit tests should *still* call this if
59 // they rely on the lock being enabled, because unit tests run in the same
60 // process in succession, and a previous test may have disabled locking.
61 static void EnableLockingOnThreadForTest();
bbudge 2013/07/29 21:02:18 This is hard to understand. How about this? // En
dmichael (off chromium) 2013/07/29 21:24:03 Much better, thanks
62
44 private: 63 private:
64 friend class content::HostGlobals;
65 // On the host side, we do not lock. This must be called at most once at
66 // startup, before other threads that may access the ProxyLock have had a
67 // chance to run.
68 static void DisableLocking();
69
45 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock); 70 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock);
46 }; 71 };
47 72
48 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing 73 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing
49 // on exit. This is for simple interfaces that don't use the 'thunk' system, 74 // on exit. This is for simple interfaces that don't use the 'thunk' system,
50 // such as PPB_Var and PPB_Core. 75 // such as PPB_Var and PPB_Core.
51 class ProxyAutoLock { 76 class ProxyAutoLock {
52 public: 77 public:
53 ProxyAutoLock() { 78 ProxyAutoLock() {
54 ProxyLock::Acquire(); 79 ProxyLock::Acquire();
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 ProxyAutoLock lock; 182 ProxyAutoLock lock;
158 { 183 {
159 // Use a scope and local Callback to ensure that the callback is cleared 184 // Use a scope and local Callback to ensure that the callback is cleared
160 // before the lock is released, even in the unlikely event that Run() 185 // before the lock is released, even in the unlikely event that Run()
161 // throws an exception. 186 // throws an exception.
162 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 187 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
163 temp_callback->Run(); 188 temp_callback->Run();
164 } 189 }
165 } 190 }
166 191
192 ~RunWhileLockedHelper() {
193 // Check that the Callback is destroyed on the same thread as where
194 // CallWhileLocked happened (if CallWhileLocked happened).
195 DCHECK(thread_checker_.CalledOnValidThread());
196 // Here we read callback_ without the lock. This is why the callback must be
197 // destroyed on the same thread where it runs. There are 2 cases where
198 // callback_ will be NULL:
199 // 1) This is the original RunWhileLockedHelper that RunWhileLocked
200 // created. When it was copied somewhere else (e.g., to a MessageLoop
201 // queue), callback_ was passed to the new copy, and the original
202 // RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs
203 // only ever have 1 owner). In this case, we don't want to acquire the
204 // lock, because we already have it.
205 // 2) callback_ has already been run via CallWhileLocked. In this case,
206 // there's no need to acquire the lock, because we don't touch any
207 // shared data.
208 if (callback_) {
209 // If the callback was not run, we still need to have the lock when we
210 // destroy the callback in case it had a Resource bound to it. This
211 // ensures that the Resource's destructor is invoked only with the lock
212 // held.
213 //
214 // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted),
215 // and these callbacks need to be usable on any thread. So we need to lock
216 // when releasing the callback to avoid ref counting races.
217 ProxyAutoLock lock;
218 callback_.reset();
219 }
220 }
167 private: 221 private:
168 scoped_ptr<CallbackType> callback_; 222 scoped_ptr<CallbackType> callback_;
169 223
170 // Used to ensure that the Callback is run and deleted on the same thread. 224 // Used to ensure that the Callback is run and deleted on the same thread.
171 base::ThreadChecker thread_checker_; 225 base::ThreadChecker thread_checker_;
172 }; 226 };
173 227
174 template <typename P1> 228 template <typename P1>
175 class RunWhileLockedHelper<void (P1)> { 229 class RunWhileLockedHelper<void (P1)> {
176 public: 230 public:
177 typedef base::Callback<void (P1)> CallbackType; 231 typedef base::Callback<void (P1)> CallbackType;
178 explicit RunWhileLockedHelper(const CallbackType& callback) 232 explicit RunWhileLockedHelper(const CallbackType& callback)
179 : callback_(new CallbackType(callback)) { 233 : callback_(new CallbackType(callback)) {
180 ProxyLock::AssertAcquired(); 234 ProxyLock::AssertAcquired();
181 thread_checker_.DetachFromThread(); 235 thread_checker_.DetachFromThread();
182 } 236 }
183 void CallWhileLocked(P1 p1) { 237 void CallWhileLocked(P1 p1) {
184 DCHECK(thread_checker_.CalledOnValidThread()); 238 DCHECK(thread_checker_.CalledOnValidThread());
185 ProxyAutoLock lock; 239 ProxyAutoLock lock;
186 { 240 {
187 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 241 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
188 temp_callback->Run(p1); 242 temp_callback->Run(p1);
189 } 243 }
190 } 244 }
191 245 ~RunWhileLockedHelper() {
246 DCHECK(thread_checker_.CalledOnValidThread());
247 if (callback_) {
248 ProxyAutoLock lock;
249 callback_.reset();
250 }
251 }
192 private: 252 private:
193 scoped_ptr<CallbackType> callback_; 253 scoped_ptr<CallbackType> callback_;
194 base::ThreadChecker thread_checker_; 254 base::ThreadChecker thread_checker_;
195 }; 255 };
196 256
197 template <typename P1, typename P2> 257 template <typename P1, typename P2>
198 class RunWhileLockedHelper<void (P1, P2)> { 258 class RunWhileLockedHelper<void (P1, P2)> {
199 public: 259 public:
200 typedef base::Callback<void (P1, P2)> CallbackType; 260 typedef base::Callback<void (P1, P2)> CallbackType;
201 explicit RunWhileLockedHelper(const CallbackType& callback) 261 explicit RunWhileLockedHelper(const CallbackType& callback)
202 : callback_(new CallbackType(callback)) { 262 : callback_(new CallbackType(callback)) {
203 ProxyLock::AssertAcquired(); 263 ProxyLock::AssertAcquired();
204 thread_checker_.DetachFromThread(); 264 thread_checker_.DetachFromThread();
205 } 265 }
206 void CallWhileLocked(P1 p1, P2 p2) { 266 void CallWhileLocked(P1 p1, P2 p2) {
207 DCHECK(thread_checker_.CalledOnValidThread()); 267 DCHECK(thread_checker_.CalledOnValidThread());
208 ProxyAutoLock lock; 268 ProxyAutoLock lock;
209 { 269 {
210 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 270 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
211 temp_callback->Run(p1, p2); 271 temp_callback->Run(p1, p2);
212 } 272 }
213 } 273 }
214 274 ~RunWhileLockedHelper() {
275 DCHECK(thread_checker_.CalledOnValidThread());
276 if (callback_) {
277 ProxyAutoLock lock;
278 callback_.reset();
279 }
280 }
215 private: 281 private:
216 scoped_ptr<CallbackType> callback_; 282 scoped_ptr<CallbackType> callback_;
217 base::ThreadChecker thread_checker_; 283 base::ThreadChecker thread_checker_;
218 }; 284 };
219 285
220 template <typename P1, typename P2, typename P3> 286 template <typename P1, typename P2, typename P3>
221 class RunWhileLockedHelper<void (P1, P2, P3)> { 287 class RunWhileLockedHelper<void (P1, P2, P3)> {
222 public: 288 public:
223 typedef base::Callback<void (P1, P2, P3)> CallbackType; 289 typedef base::Callback<void (P1, P2, P3)> CallbackType;
224 explicit RunWhileLockedHelper(const CallbackType& callback) 290 explicit RunWhileLockedHelper(const CallbackType& callback)
225 : callback_(new CallbackType(callback)) { 291 : callback_(new CallbackType(callback)) {
226 ProxyLock::AssertAcquired(); 292 ProxyLock::AssertAcquired();
227 thread_checker_.DetachFromThread(); 293 thread_checker_.DetachFromThread();
228 } 294 }
229 void CallWhileLocked(P1 p1, P2 p2, P3 p3) { 295 void CallWhileLocked(P1 p1, P2 p2, P3 p3) {
230 DCHECK(thread_checker_.CalledOnValidThread()); 296 DCHECK(thread_checker_.CalledOnValidThread());
231 ProxyAutoLock lock; 297 ProxyAutoLock lock;
232 { 298 {
233 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 299 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
234 temp_callback->Run(p1, p2, p3); 300 temp_callback->Run(p1, p2, p3);
235 } 301 }
236 } 302 }
237 303 ~RunWhileLockedHelper() {
304 DCHECK(thread_checker_.CalledOnValidThread());
305 if (callback_) {
306 ProxyAutoLock lock;
307 callback_.reset();
308 }
309 }
238 private: 310 private:
239 scoped_ptr<CallbackType> callback_; 311 scoped_ptr<CallbackType> callback_;
240 base::ThreadChecker thread_checker_; 312 base::ThreadChecker thread_checker_;
241 }; 313 };
242 314
243 } // namespace internal 315 } // namespace internal
244 316
245 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked: 317 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
246 // 1) Locks the ProxyLock. 318 // 1) Locks the ProxyLock.
247 // 2) Runs the original Callback (forwarding arguments, if any). 319 // 2) Runs the original Callback (forwarding arguments, if any).
(...skipping 15 matching lines...) Expand all
263 // In normal usage like the above, this all should "just work". However, if you 335 // In normal usage like the above, this all should "just work". However, if you
264 // do something unusual, you may get a runtime crash due to deadlock. Here are 336 // do something unusual, you may get a runtime crash due to deadlock. Here are
265 // the ways that the returned Callback must be used to avoid a deadlock: 337 // the ways that the returned Callback must be used to avoid a deadlock:
266 // (1) copied to another Callback. After that, the original callback can be 338 // (1) copied to another Callback. After that, the original callback can be
267 // destroyed with or without the proxy lock acquired, while the newly assigned 339 // destroyed with or without the proxy lock acquired, while the newly assigned
268 // callback has to conform to these same restrictions. Or 340 // callback has to conform to these same restrictions. Or
269 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop 341 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop
270 // and run there). The callback must be destroyed on the same thread where it 342 // and run there). The callback must be destroyed on the same thread where it
271 // was run (but can be destroyed with or without the proxy lock acquired). Or 343 // was run (but can be destroyed with or without the proxy lock acquired). Or
272 // (3) destroyed without the proxy lock acquired. 344 // (3) destroyed without the proxy lock acquired.
273 // TODO(dmichael): This won't actually fail until
274 // https://codereview.chromium.org/19492014/ lands.
275 template <class FunctionType> 345 template <class FunctionType>
276 inline base::Callback<FunctionType> 346 inline base::Callback<FunctionType>
277 RunWhileLocked(const base::Callback<FunctionType>& callback) { 347 RunWhileLocked(const base::Callback<FunctionType>& callback) {
278 internal::RunWhileLockedHelper<FunctionType>* helper = 348 internal::RunWhileLockedHelper<FunctionType>* helper =
279 new internal::RunWhileLockedHelper<FunctionType>(callback); 349 new internal::RunWhileLockedHelper<FunctionType>(callback);
280 return base::Bind( 350 return base::Bind(
281 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, 351 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
282 base::Owned(helper)); 352 base::Owned(helper));
283 } 353 }
284 354
285 } // namespace ppapi 355 } // namespace ppapi
286 356
287 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 357 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698