Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ppapi / shared_impl / proxy_lock.h
blob66d4a739047b0b1d7d64a5ba7e640f5944bef1ef
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
3 // found in the LICENSE file.
5 #ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_
6 #define PPAPI_SHARED_IMPL_PROXY_LOCK_H_
8 #include "base/basictypes.h"
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/threading/thread_checker.h"
14 #include "ppapi/shared_impl/ppapi_shared_export.h"
16 namespace base {
17 class Lock;
20 namespace content {
21 class HostGlobals;
24 namespace ppapi {
26 // This is the one lock to rule them all for the ppapi proxy. All PPB interface
27 // functions that need to be synchronized should lock this lock on entry. This
28 // is normally accomplished by using an appropriate Enter RAII object at the
29 // beginning of each thunk function.
31 // TODO(dmichael): If this turns out to be too slow and contentious, we'll want
32 // to use multiple locks. E.g., one for the var tracker, one for the resource
33 // tracker, etc.
34 class PPAPI_SHARED_EXPORT ProxyLock {
35 public:
36 // Return the global ProxyLock. Normally, you should not access this
37 // directly but instead use ProxyAutoLock or ProxyAutoUnlock. But sometimes
38 // you need access to the ProxyLock, for example to create a condition
39 // variable.
40 static base::Lock* Get();
42 // Acquire the proxy lock. If it is currently held by another thread, block
43 // until it is available. If the lock has not been set using the 'Set' method,
44 // this operation does nothing. That is the normal case for the host side;
45 // see PluginResourceTracker for where the lock gets set for the out-of-
46 // process plugin case.
47 static void Acquire();
48 // Relinquish the proxy lock. If the lock has not been set, this does nothing.
49 static void Release();
51 // Assert that the lock is owned by the current thread (in the plugin
52 // process). Does nothing when running in-process (or in the host process).
53 static void AssertAcquired();
54 static void AssertAcquiredDebugOnly() {
55 #ifndef NDEBUG
56 AssertAcquired();
57 #endif
60 // We have some unit tests where one thread pretends to be the host and one
61 // pretends to be the plugin. This allows the lock to do nothing on only one
62 // thread to support these tests. See TwoWayTest for more information.
63 class PPAPI_SHARED_EXPORT LockingDisablerForTest {
64 public:
65 LockingDisablerForTest();
66 ~LockingDisablerForTest();
69 private:
70 friend class content::HostGlobals;
71 // On the host side, we do not lock. This must be called at most once at
72 // startup, before other threads that may access the ProxyLock have had a
73 // chance to run.
74 static void DisableLocking();
76 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock);
79 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing
80 // on exit. This is for simple interfaces that don't use the 'thunk' system,
81 // such as PPB_Var and PPB_Core.
82 class ProxyAutoLock {
83 public:
84 ProxyAutoLock() { ProxyLock::Acquire(); }
85 ~ProxyAutoLock() { ProxyLock::Release(); }
87 private:
88 DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock);
91 // The inverse of the above; unlock on construction, lock on destruction. This
92 // is useful for calling out to the plugin, when we need to unlock but ensure
93 // that we re-acquire the lock when the plugin is returns or raises an
94 // exception.
95 class ProxyAutoUnlock {
96 public:
97 ProxyAutoUnlock() { ProxyLock::Release(); }
98 ~ProxyAutoUnlock() { ProxyLock::Acquire(); }
100 private:
101 DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock);
104 // A set of function template overloads for invoking a function pointer while
105 // the ProxyLock is unlocked. This assumes that the luck is held.
106 // CallWhileUnlocked unlocks the ProxyLock just before invoking the given
107 // function. The lock is immediately re-acquired when the invoked function
108 // function returns. CallWhileUnlocked returns whatever the given function
109 // returned.
111 // Example usage:
112 // *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent,
113 // instance,
114 // resource->pp_resource());
115 template <class ReturnType>
116 ReturnType CallWhileUnlocked(ReturnType (*function)()) {
117 ProxyAutoUnlock unlock;
118 return function();
120 // Note we use 2 types for the params, even though for the most part we expect
121 // A1 to match P1. We let the compiler determine if P1 can convert safely to
122 // A1. This allows callers to avoid having to do things like
123 // const_cast to add const.
124 template <class ReturnType, class A1, class P1>
125 ReturnType CallWhileUnlocked(ReturnType (*function)(A1), const P1& p1) {
126 ProxyAutoUnlock unlock;
127 return function(p1);
129 template <class ReturnType, class A1, class A2, class P1, class P2>
130 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2),
131 const P1& p1,
132 const P2& p2) {
133 ProxyAutoUnlock unlock;
134 return function(p1, p2);
136 template <class ReturnType, class A1, class A2, class A3, class P1, class P2,
137 class P3>
138 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3),
139 const P1& p1,
140 const P2& p2,
141 const P3& p3) {
142 ProxyAutoUnlock unlock;
143 return function(p1, p2, p3);
145 template <class ReturnType, class A1, class A2, class A3, class A4, class P1,
146 class P2, class P3, class P4>
147 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3, A4),
148 const P1& p1,
149 const P2& p2,
150 const P3& p3,
151 const P4& p4) {
152 ProxyAutoUnlock unlock;
153 return function(p1, p2, p3, p4);
155 template <class ReturnType, class A1, class A2, class A3, class A4, class A5,
156 class P1, class P2, class P3, class P4, class P5>
157 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3, A4, A5),
158 const P1& p1,
159 const P2& p2,
160 const P3& p3,
161 const P4& p4,
162 const P5& p5) {
163 ProxyAutoUnlock unlock;
164 return function(p1, p2, p3, p4, p5);
166 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure);
168 namespace internal {
170 template <typename RunType>
171 class RunWhileLockedHelper;
173 // A helper class to ensure that a callback is always run and destroyed while
174 // the ProxyLock is held. A callback that is bound with ref-counted Var or
175 // Resource parameters may invoke methods on the VarTracker or the
176 // ResourceTracker in its destructor, and these require the ProxyLock.
177 template <>
178 class RunWhileLockedHelper<void()> {
179 public:
180 typedef base::Callback<void()> CallbackType;
181 explicit RunWhileLockedHelper(const CallbackType& callback)
182 : callback_(new CallbackType(callback)) {
183 // CallWhileLocked and destruction might happen on a different thread from
184 // creation.
185 thread_checker_.DetachFromThread();
187 static void CallWhileLocked(scoped_ptr<RunWhileLockedHelper> ptr) {
188 // Bind thread_checker_ to this thread so we can check in the destructor.
189 // *If* the callback gets invoked, it's important that RunWhileLockedHelper
190 // is destroyed on the same thread (see the comments in the destructor).
191 DCHECK(ptr->thread_checker_.CalledOnValidThread());
192 ProxyAutoLock lock;
194 // Use a scope and local Callback to ensure that the callback is cleared
195 // before the lock is released, even in the unlikely event that Run()
196 // throws an exception.
197 scoped_ptr<CallbackType> temp_callback(ptr->callback_.Pass());
198 temp_callback->Run();
202 ~RunWhileLockedHelper() {
203 // Check that the Callback is destroyed on the same thread as where
204 // CallWhileLocked happened if CallWhileLocked happened. If we weren't
205 // invoked, thread_checked_ isn't bound to a thread.
206 DCHECK(thread_checker_.CalledOnValidThread());
207 // Here we read callback_ without the lock. This is why the callback must be
208 // destroyed on the same thread where it runs. Note that callback_ will be
209 // NULL if it has already been run via CallWhileLocked. In this case,
210 // there's no need to acquire the lock, because we don't touch any shared
211 // data.
212 if (callback_) {
213 // If the callback was *not* run, we're in a case where the task queue
214 // we got pushed to has been destroyed (e.g., the thread is shut down and
215 // its MessageLoop destroyed before all tasks have run.)
217 // We still need to have the lock when we destroy the callback:
218 // - Because Resource and Var inherit RefCounted (not
219 // ThreadSafeRefCounted).
220 // - Because if the callback owns the last ref to a Resource, it will
221 // call the ResourceTracker and also the Resource's destructor, which
222 // both require the ProxyLock.
223 ProxyAutoLock lock;
224 callback_.reset();
228 private:
229 DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
230 scoped_ptr<CallbackType> callback_;
232 // Used to ensure that the Callback is run and deleted on the same thread.
233 base::ThreadChecker thread_checker_;
236 template <typename P1>
237 class RunWhileLockedHelper<void(P1)> {
238 public:
239 typedef base::Callback<void(P1)> CallbackType;
240 explicit RunWhileLockedHelper(const CallbackType& callback)
241 : callback_(new CallbackType(callback)) {
242 thread_checker_.DetachFromThread();
244 static void CallWhileLocked(scoped_ptr<RunWhileLockedHelper> ptr, P1 p1) {
245 DCHECK(ptr->thread_checker_.CalledOnValidThread());
246 ProxyAutoLock lock;
248 scoped_ptr<CallbackType> temp_callback(ptr->callback_.Pass());
249 temp_callback->Run(p1);
252 ~RunWhileLockedHelper() {
253 DCHECK(thread_checker_.CalledOnValidThread());
254 if (callback_) {
255 ProxyAutoLock lock;
256 callback_.reset();
260 private:
261 DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
262 scoped_ptr<CallbackType> callback_;
263 base::ThreadChecker thread_checker_;
266 template <typename P1, typename P2>
267 class RunWhileLockedHelper<void(P1, P2)> {
268 public:
269 typedef base::Callback<void(P1, P2)> CallbackType;
270 explicit RunWhileLockedHelper(const CallbackType& callback)
271 : callback_(new CallbackType(callback)) {
272 thread_checker_.DetachFromThread();
274 static void CallWhileLocked(
275 scoped_ptr<RunWhileLockedHelper> ptr, P1 p1, P2 p2) {
276 DCHECK(ptr->thread_checker_.CalledOnValidThread());
277 ProxyAutoLock lock;
279 scoped_ptr<CallbackType> temp_callback(ptr->callback_.Pass());
280 temp_callback->Run(p1, p2);
283 ~RunWhileLockedHelper() {
284 DCHECK(thread_checker_.CalledOnValidThread());
285 if (callback_) {
286 ProxyAutoLock lock;
287 callback_.reset();
291 private:
292 DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
293 scoped_ptr<CallbackType> callback_;
294 base::ThreadChecker thread_checker_;
297 template <typename P1, typename P2, typename P3>
298 class RunWhileLockedHelper<void(P1, P2, P3)> {
299 public:
300 typedef base::Callback<void(P1, P2, P3)> CallbackType;
301 explicit RunWhileLockedHelper(const CallbackType& callback)
302 : callback_(new CallbackType(callback)) {
303 thread_checker_.DetachFromThread();
305 static void CallWhileLocked(
306 scoped_ptr<RunWhileLockedHelper> ptr, P1 p1, P2 p2, P3 p3) {
307 DCHECK(ptr->thread_checker_.CalledOnValidThread());
308 ProxyAutoLock lock;
310 scoped_ptr<CallbackType> temp_callback(ptr->callback_.Pass());
311 temp_callback->Run(p1, p2, p3);
314 ~RunWhileLockedHelper() {
315 DCHECK(thread_checker_.CalledOnValidThread());
316 if (callback_) {
317 ProxyAutoLock lock;
318 callback_.reset();
322 private:
323 DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
324 scoped_ptr<CallbackType> callback_;
325 base::ThreadChecker thread_checker_;
328 } // namespace internal
330 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
331 // 1) Locks the ProxyLock.
332 // 2) Runs the original Callback (forwarding arguments, if any).
333 // 3) Clears the original Callback (while the lock is held).
334 // 4) Unlocks the ProxyLock.
335 // Note that it's important that the callback is cleared in step (3), in case
336 // clearing the Callback causes a destructor (e.g., for a Resource) to run,
337 // which should hold the ProxyLock to avoid data races.
339 // This is for cases where you want to run a task or store a Callback, but you
340 // want to ensure that the ProxyLock is acquired for the duration of the task
341 // that the Callback runs.
342 // EXAMPLE USAGE:
343 // GetMainThreadMessageLoop()->PostDelayedTask(
344 // FROM_HERE,
345 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)),
346 // delay_in_ms);
348 // In normal usage like the above, this all should "just work". However, if you
349 // do something unusual, you may get a runtime crash due to deadlock. Here are
350 // the ways that the returned Callback must be used to avoid a deadlock:
351 // (1) copied to another Callback. After that, the original callback can be
352 // destroyed with or without the proxy lock acquired, while the newly assigned
353 // callback has to conform to these same restrictions. Or
354 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop
355 // and run there). The callback must be destroyed on the same thread where it
356 // was run (but can be destroyed with or without the proxy lock acquired). Or
357 // (3) destroyed without the proxy lock acquired.
358 template <class FunctionType>
359 inline base::Callback<FunctionType> RunWhileLocked(
360 const base::Callback<FunctionType>& callback) {
361 // NOTE: the reason we use "scoped_ptr" here instead of letting the callback
362 // own it via base::Owned is kind of subtle. Imagine for the moment that we
363 // call RunWhileLocked without the ProxyLock:
364 // {
365 // base::Callback<void ()> local_callback = base::Bind(&Foo);
366 // some_task_runner.PostTask(FROM_HERE, RunWhileLocked(local_callback));
367 // }
368 // In this case, since we don't have a lock synchronizing us, it's possible
369 // for the callback to run on the other thread before we return and destroy
370 // |local_callback|. The important thing here is that even though the other
371 // thread gets a copy of the callback, the internal "BindState" of the
372 // callback is refcounted and shared between all copies of the callback. So
373 // in that case, if we used base::Owned, we might delete RunWhileLockedHelper
374 // on this thread, which will violate the RunWhileLockedHelper's assumption
375 // that it is destroyed on the same thread where it is run.
376 scoped_ptr<internal::RunWhileLockedHelper<FunctionType>> helper(
377 new internal::RunWhileLockedHelper<FunctionType>(callback));
378 return base::Bind(
379 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
380 base::Passed(helper.Pass()));
383 } // namespace ppapi
385 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_