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

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: Remove static initializer Created 7 years, 3 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
« no previous file with comments | « ppapi/shared_impl/ppapi_globals.h ('k') | ppapi/shared_impl/proxy_lock.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 global ProxyLock. Normally, you should not access this
36 // directly but instead use ProxyAutoLock or ProxyAutoUnlock. But sometimes
37 // you need access to the ProxyLock, for example to create a condition
38 // variable.
39 static base::Lock* Get();
40
31 // Acquire the proxy lock. If it is currently held by another thread, block 41 // 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, 42 // 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; 43 // 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- 44 // see PluginResourceTracker for where the lock gets set for the out-of-
35 // process plugin case. 45 // process plugin case.
36 static void Acquire(); 46 static void Acquire();
37 // Relinquish the proxy lock. If the lock has not been set, this does nothing. 47 // Relinquish the proxy lock. If the lock has not been set, this does nothing.
38 static void Release(); 48 static void Release();
39 49
40 // Assert that the lock is owned by the current thread (in the plugin 50 // 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). 51 // process). Does nothing when running in-process (or in the host process).
42 static void AssertAcquired(); 52 static void AssertAcquired();
43 53
54 // We have some unit tests where one thread pretends to be the host and one
55 // pretends to be the plugin. This allows the lock to do nothing on only one
56 // thread to support these tests. See TwoWayTest for more information.
57 static void DisableLockingOnThreadForTest();
58
59 // Enables locking on the current thread. Although locking is enabled by
60 // default, unit tests that rely on the lock being enabled should *still*
61 // call this, since a previous test may have disabled locking.
62 static void EnableLockingOnThreadForTest();
63
44 private: 64 private:
65 friend class content::HostGlobals;
66 // On the host side, we do not lock. This must be called at most once at
67 // startup, before other threads that may access the ProxyLock have had a
68 // chance to run.
69 static void DisableLocking();
70
45 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock); 71 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock);
46 }; 72 };
47 73
48 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing 74 // 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, 75 // on exit. This is for simple interfaces that don't use the 'thunk' system,
50 // such as PPB_Var and PPB_Core. 76 // such as PPB_Var and PPB_Core.
51 class ProxyAutoLock { 77 class ProxyAutoLock {
52 public: 78 public:
53 ProxyAutoLock() { 79 ProxyAutoLock() {
54 ProxyLock::Acquire(); 80 ProxyLock::Acquire();
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 ProxyAutoLock lock; 183 ProxyAutoLock lock;
158 { 184 {
159 // Use a scope and local Callback to ensure that the callback is cleared 185 // 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() 186 // before the lock is released, even in the unlikely event that Run()
161 // throws an exception. 187 // throws an exception.
162 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 188 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
163 temp_callback->Run(); 189 temp_callback->Run();
164 } 190 }
165 } 191 }
166 192
193 ~RunWhileLockedHelper() {
194 // Check that the Callback is destroyed on the same thread as where
195 // CallWhileLocked happened (if CallWhileLocked happened).
196 DCHECK(thread_checker_.CalledOnValidThread());
197 // Here we read callback_ without the lock. This is why the callback must be
198 // destroyed on the same thread where it runs. There are 2 cases where
199 // callback_ will be NULL:
200 // 1) This is the original RunWhileLockedHelper that RunWhileLocked
201 // created. When it was copied somewhere else (e.g., to a MessageLoop
202 // queue), callback_ was passed to the new copy, and the original
203 // RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs
204 // only ever have 1 owner). In this case, we don't want to acquire the
205 // lock, because we already have it.
206 // 2) callback_ has already been run via CallWhileLocked. In this case,
207 // there's no need to acquire the lock, because we don't touch any
208 // shared data.
209 if (callback_) {
210 // If the callback was not run, we still need to have the lock when we
211 // destroy the callback in case it had a Resource bound to it. This
212 // ensures that the Resource's destructor is invoked only with the lock
213 // held.
214 //
215 // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted),
216 // and these callbacks need to be usable on any thread. So we need to lock
217 // when releasing the callback to avoid ref counting races.
218 ProxyAutoLock lock;
219 callback_.reset();
220 }
221 }
167 private: 222 private:
168 scoped_ptr<CallbackType> callback_; 223 scoped_ptr<CallbackType> callback_;
169 224
170 // Used to ensure that the Callback is run and deleted on the same thread. 225 // Used to ensure that the Callback is run and deleted on the same thread.
171 base::ThreadChecker thread_checker_; 226 base::ThreadChecker thread_checker_;
172 }; 227 };
173 228
174 template <typename P1> 229 template <typename P1>
175 class RunWhileLockedHelper<void (P1)> { 230 class RunWhileLockedHelper<void (P1)> {
176 public: 231 public:
177 typedef base::Callback<void (P1)> CallbackType; 232 typedef base::Callback<void (P1)> CallbackType;
178 explicit RunWhileLockedHelper(const CallbackType& callback) 233 explicit RunWhileLockedHelper(const CallbackType& callback)
179 : callback_(new CallbackType(callback)) { 234 : callback_(new CallbackType(callback)) {
180 ProxyLock::AssertAcquired(); 235 ProxyLock::AssertAcquired();
181 thread_checker_.DetachFromThread(); 236 thread_checker_.DetachFromThread();
182 } 237 }
183 void CallWhileLocked(P1 p1) { 238 void CallWhileLocked(P1 p1) {
184 DCHECK(thread_checker_.CalledOnValidThread()); 239 DCHECK(thread_checker_.CalledOnValidThread());
185 ProxyAutoLock lock; 240 ProxyAutoLock lock;
186 { 241 {
187 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 242 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
188 temp_callback->Run(p1); 243 temp_callback->Run(p1);
189 } 244 }
190 } 245 }
191 246 ~RunWhileLockedHelper() {
247 DCHECK(thread_checker_.CalledOnValidThread());
248 if (callback_) {
249 ProxyAutoLock lock;
250 callback_.reset();
251 }
252 }
192 private: 253 private:
193 scoped_ptr<CallbackType> callback_; 254 scoped_ptr<CallbackType> callback_;
194 base::ThreadChecker thread_checker_; 255 base::ThreadChecker thread_checker_;
195 }; 256 };
196 257
197 template <typename P1, typename P2> 258 template <typename P1, typename P2>
198 class RunWhileLockedHelper<void (P1, P2)> { 259 class RunWhileLockedHelper<void (P1, P2)> {
199 public: 260 public:
200 typedef base::Callback<void (P1, P2)> CallbackType; 261 typedef base::Callback<void (P1, P2)> CallbackType;
201 explicit RunWhileLockedHelper(const CallbackType& callback) 262 explicit RunWhileLockedHelper(const CallbackType& callback)
202 : callback_(new CallbackType(callback)) { 263 : callback_(new CallbackType(callback)) {
203 ProxyLock::AssertAcquired(); 264 ProxyLock::AssertAcquired();
204 thread_checker_.DetachFromThread(); 265 thread_checker_.DetachFromThread();
205 } 266 }
206 void CallWhileLocked(P1 p1, P2 p2) { 267 void CallWhileLocked(P1 p1, P2 p2) {
207 DCHECK(thread_checker_.CalledOnValidThread()); 268 DCHECK(thread_checker_.CalledOnValidThread());
208 ProxyAutoLock lock; 269 ProxyAutoLock lock;
209 { 270 {
210 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 271 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
211 temp_callback->Run(p1, p2); 272 temp_callback->Run(p1, p2);
212 } 273 }
213 } 274 }
214 275 ~RunWhileLockedHelper() {
276 DCHECK(thread_checker_.CalledOnValidThread());
277 if (callback_) {
278 ProxyAutoLock lock;
279 callback_.reset();
280 }
281 }
215 private: 282 private:
216 scoped_ptr<CallbackType> callback_; 283 scoped_ptr<CallbackType> callback_;
217 base::ThreadChecker thread_checker_; 284 base::ThreadChecker thread_checker_;
218 }; 285 };
219 286
220 template <typename P1, typename P2, typename P3> 287 template <typename P1, typename P2, typename P3>
221 class RunWhileLockedHelper<void (P1, P2, P3)> { 288 class RunWhileLockedHelper<void (P1, P2, P3)> {
222 public: 289 public:
223 typedef base::Callback<void (P1, P2, P3)> CallbackType; 290 typedef base::Callback<void (P1, P2, P3)> CallbackType;
224 explicit RunWhileLockedHelper(const CallbackType& callback) 291 explicit RunWhileLockedHelper(const CallbackType& callback)
225 : callback_(new CallbackType(callback)) { 292 : callback_(new CallbackType(callback)) {
226 ProxyLock::AssertAcquired(); 293 ProxyLock::AssertAcquired();
227 thread_checker_.DetachFromThread(); 294 thread_checker_.DetachFromThread();
228 } 295 }
229 void CallWhileLocked(P1 p1, P2 p2, P3 p3) { 296 void CallWhileLocked(P1 p1, P2 p2, P3 p3) {
230 DCHECK(thread_checker_.CalledOnValidThread()); 297 DCHECK(thread_checker_.CalledOnValidThread());
231 ProxyAutoLock lock; 298 ProxyAutoLock lock;
232 { 299 {
233 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 300 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
234 temp_callback->Run(p1, p2, p3); 301 temp_callback->Run(p1, p2, p3);
235 } 302 }
236 } 303 }
237 304 ~RunWhileLockedHelper() {
305 DCHECK(thread_checker_.CalledOnValidThread());
306 if (callback_) {
307 ProxyAutoLock lock;
308 callback_.reset();
309 }
310 }
238 private: 311 private:
239 scoped_ptr<CallbackType> callback_; 312 scoped_ptr<CallbackType> callback_;
240 base::ThreadChecker thread_checker_; 313 base::ThreadChecker thread_checker_;
241 }; 314 };
242 315
243 } // namespace internal 316 } // namespace internal
244 317
245 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked: 318 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
246 // 1) Locks the ProxyLock. 319 // 1) Locks the ProxyLock.
247 // 2) Runs the original Callback (forwarding arguments, if any). 320 // 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 336 // 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 337 // 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: 338 // 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 339 // (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 340 // destroyed with or without the proxy lock acquired, while the newly assigned
268 // callback has to conform to these same restrictions. Or 341 // callback has to conform to these same restrictions. Or
269 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop 342 // (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 343 // 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 344 // was run (but can be destroyed with or without the proxy lock acquired). Or
272 // (3) destroyed without the proxy lock acquired. 345 // (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> 346 template <class FunctionType>
276 inline base::Callback<FunctionType> 347 inline base::Callback<FunctionType>
277 RunWhileLocked(const base::Callback<FunctionType>& callback) { 348 RunWhileLocked(const base::Callback<FunctionType>& callback) {
278 internal::RunWhileLockedHelper<FunctionType>* helper = 349 internal::RunWhileLockedHelper<FunctionType>* helper =
279 new internal::RunWhileLockedHelper<FunctionType>(callback); 350 new internal::RunWhileLockedHelper<FunctionType>(callback);
280 return base::Bind( 351 return base::Bind(
281 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, 352 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
282 base::Owned(helper)); 353 base::Owned(helper));
283 } 354 }
284 355
285 } // namespace ppapi 356 } // namespace ppapi
286 357
287 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 358 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_
OLDNEW
« no previous file with comments | « ppapi/shared_impl/ppapi_globals.h ('k') | ppapi/shared_impl/proxy_lock.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698