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.
4
5#ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_
6#define PPAPI_SHARED_IMPL_PROXY_LOCK_H_
7
8#include "base/basictypes.h"
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/threading/thread_checker.h"
12
13#include "ppapi/shared_impl/ppapi_shared_export.h"
14
15namespace base {
16class Lock;
17}
18
19namespace content {
20class HostGlobals;
21}
22
23namespace ppapi {
24
25// This is the one lock to rule them all for the ppapi proxy. All PPB interface
26// functions that need to be synchronized should lock this lock on entry. This
27// is normally accomplished by using an appropriate Enter RAII object at the
28// beginning of each thunk function.
29//
30// TODO(dmichael): If this turns out to be too slow and contentious, we'll want
31// to use multiple locks. E.g., one for the var tracker, one for the resource
32// tracker, etc.
33class PPAPI_SHARED_EXPORT ProxyLock {
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
41  // Acquire the proxy lock. If it is currently held by another thread, block
42  // until it is available. If the lock has not been set using the 'Set' method,
43  // this operation does nothing. That is the normal case for the host side;
44  // see PluginResourceTracker for where the lock gets set for the out-of-
45  // process plugin case.
46  static void Acquire();
47  // Relinquish the proxy lock. If the lock has not been set, this does nothing.
48  static void Release();
49
50  // Assert that the lock is owned by the current thread (in the plugin
51  // process). Does nothing when running in-process (or in the host process).
52  static void AssertAcquired();
53  static void AssertAcquiredDebugOnly() {
54#ifndef NDEBUG
55    AssertAcquired();
56#endif
57  }
58
59  // We have some unit tests where one thread pretends to be the host and one
60  // pretends to be the plugin. This allows the lock to do nothing on only one
61  // thread to support these tests. See TwoWayTest for more information.
62  static void DisableLockingOnThreadForTest();
63
64  // Enables locking on the current thread. Although locking is enabled by
65  // default, unit tests that rely on the lock being enabled should *still*
66  // call this, since a previous test may have disabled locking.
67  static void EnableLockingOnThreadForTest();
68
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();
75
76  DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock);
77};
78
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.
82class ProxyAutoLock {
83 public:
84  ProxyAutoLock() { ProxyLock::Acquire(); }
85  ~ProxyAutoLock() { ProxyLock::Release(); }
86
87 private:
88  DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock);
89};
90
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.
95class ProxyAutoUnlock {
96 public:
97  ProxyAutoUnlock() { ProxyLock::Release(); }
98  ~ProxyAutoUnlock() { ProxyLock::Acquire(); }
99
100 private:
101  DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock);
102};
103
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.
110//
111// Example usage:
112//   *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent,
113//                               instance,
114//                               resource->pp_resource());
115template <class ReturnType>
116ReturnType CallWhileUnlocked(ReturnType (*function)()) {
117  ProxyAutoUnlock unlock;
118  return function();
119}
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.
124template <class ReturnType, class A1, class P1>
125ReturnType CallWhileUnlocked(ReturnType (*function)(A1), const P1& p1) {
126  ProxyAutoUnlock unlock;
127  return function(p1);
128}
129template <class ReturnType, class A1, class A2, class P1, class P2>
130ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2),
131                             const P1& p1,
132                             const P2& p2) {
133  ProxyAutoUnlock unlock;
134  return function(p1, p2);
135}
136template <class ReturnType, class A1, class A2, class A3, class P1, class P2,
137          class P3>
138ReturnType 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);
144}
145template <class ReturnType, class A1, class A2, class A3, class A4, class P1,
146          class P2, class P3, class P4>
147ReturnType 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);
154}
155template <class ReturnType, class A1, class A2, class A3, class A4, class A5,
156          class P1, class P2, class P3, class P4, class P5>
157ReturnType 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);
165}
166void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure);
167
168namespace internal {
169
170template <typename RunType>
171class RunWhileLockedHelper;
172
173template <>
174class RunWhileLockedHelper<void()> {
175 public:
176  typedef base::Callback<void()> CallbackType;
177  explicit RunWhileLockedHelper(const CallbackType& callback)
178      : callback_(new CallbackType(callback)) {
179    // Copying |callback| may adjust reference counts for bound Vars or
180    // Resources; we should have the lock already.
181    ProxyLock::AssertAcquired();
182    // CallWhileLocked and destruction might happen on a different thread from
183    // creation.
184    thread_checker_.DetachFromThread();
185  }
186  void CallWhileLocked() {
187    // Bind thread_checker_ to this thread so we can check in the destructor.
188    DCHECK(thread_checker_.CalledOnValidThread());
189    ProxyAutoLock lock;
190    {
191      // Use a scope and local Callback to ensure that the callback is cleared
192      // before the lock is released, even in the unlikely event that Run()
193      // throws an exception.
194      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
195      temp_callback->Run();
196    }
197  }
198
199  ~RunWhileLockedHelper() {
200    // Check that the Callback is destroyed on the same thread as where
201    // CallWhileLocked happened (if CallWhileLocked happened).
202    DCHECK(thread_checker_.CalledOnValidThread());
203    // Here we read callback_ without the lock. This is why the callback must be
204    // destroyed on the same thread where it runs. There are 2 cases where
205    // callback_ will be NULL:
206    //   1) This is the original RunWhileLockedHelper that RunWhileLocked
207    //      created. When it was copied somewhere else (e.g., to a MessageLoop
208    //      queue), callback_ was passed to the new copy, and the original
209    //      RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs
210    //      only ever have 1 owner). In this case, we don't want to acquire the
211    //      lock, because we already have it.
212    //   2) callback_ has already been run via CallWhileLocked. In this case,
213    //      there's no need to acquire the lock, because we don't touch any
214    //      shared data.
215    if (callback_) {
216      // If the callback was not run, we still need to have the lock when we
217      // destroy the callback in case it had a Resource bound to it. This
218      // ensures that the Resource's destructor is invoked only with the lock
219      // held.
220      //
221      // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted),
222      // and these callbacks need to be usable on any thread. So we need to lock
223      // when releasing the callback to avoid ref counting races.
224      ProxyAutoLock lock;
225      callback_.reset();
226    }
227  }
228
229 private:
230  scoped_ptr<CallbackType> callback_;
231
232  // Used to ensure that the Callback is run and deleted on the same thread.
233  base::ThreadChecker thread_checker_;
234};
235
236template <typename P1>
237class RunWhileLockedHelper<void(P1)> {
238 public:
239  typedef base::Callback<void(P1)> CallbackType;
240  explicit RunWhileLockedHelper(const CallbackType& callback)
241      : callback_(new CallbackType(callback)) {
242    ProxyLock::AssertAcquired();
243    thread_checker_.DetachFromThread();
244  }
245  void CallWhileLocked(P1 p1) {
246    DCHECK(thread_checker_.CalledOnValidThread());
247    ProxyAutoLock lock;
248    {
249      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
250      temp_callback->Run(p1);
251    }
252  }
253  ~RunWhileLockedHelper() {
254    DCHECK(thread_checker_.CalledOnValidThread());
255    if (callback_) {
256      ProxyAutoLock lock;
257      callback_.reset();
258    }
259  }
260
261 private:
262  scoped_ptr<CallbackType> callback_;
263  base::ThreadChecker thread_checker_;
264};
265
266template <typename P1, typename P2>
267class 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    ProxyLock::AssertAcquired();
273    thread_checker_.DetachFromThread();
274  }
275  void CallWhileLocked(P1 p1, P2 p2) {
276    DCHECK(thread_checker_.CalledOnValidThread());
277    ProxyAutoLock lock;
278    {
279      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
280      temp_callback->Run(p1, p2);
281    }
282  }
283  ~RunWhileLockedHelper() {
284    DCHECK(thread_checker_.CalledOnValidThread());
285    if (callback_) {
286      ProxyAutoLock lock;
287      callback_.reset();
288    }
289  }
290
291 private:
292  scoped_ptr<CallbackType> callback_;
293  base::ThreadChecker thread_checker_;
294};
295
296template <typename P1, typename P2, typename P3>
297class RunWhileLockedHelper<void(P1, P2, P3)> {
298 public:
299  typedef base::Callback<void(P1, P2, P3)> CallbackType;
300  explicit RunWhileLockedHelper(const CallbackType& callback)
301      : callback_(new CallbackType(callback)) {
302    ProxyLock::AssertAcquired();
303    thread_checker_.DetachFromThread();
304  }
305  void CallWhileLocked(P1 p1, P2 p2, P3 p3) {
306    DCHECK(thread_checker_.CalledOnValidThread());
307    ProxyAutoLock lock;
308    {
309      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
310      temp_callback->Run(p1, p2, p3);
311    }
312  }
313  ~RunWhileLockedHelper() {
314    DCHECK(thread_checker_.CalledOnValidThread());
315    if (callback_) {
316      ProxyAutoLock lock;
317      callback_.reset();
318    }
319  }
320
321 private:
322  scoped_ptr<CallbackType> callback_;
323  base::ThreadChecker thread_checker_;
324};
325
326}  // namespace internal
327
328// RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
329//  1) Locks the ProxyLock.
330//  2) Runs the original Callback (forwarding arguments, if any).
331//  3) Clears the original Callback (while the lock is held).
332//  4) Unlocks the ProxyLock.
333// Note that it's important that the callback is cleared in step (3), in case
334// clearing the Callback causes a destructor (e.g., for a Resource) to run,
335// which should hold the ProxyLock to avoid data races.
336//
337// This is for cases where you want to run a task or store a Callback, but you
338// want to ensure that the ProxyLock is acquired for the duration of the task
339// that the Callback runs.
340// EXAMPLE USAGE:
341//   GetMainThreadMessageLoop()->PostDelayedTask(
342//     FROM_HERE,
343//     RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)),
344//     delay_in_ms);
345//
346// In normal usage like the above, this all should "just work". However, if you
347// do something unusual, you may get a runtime crash due to deadlock. Here are
348// the ways that the returned Callback must be used to avoid a deadlock:
349// (1) copied to another Callback. After that, the original callback can be
350// destroyed with or without the proxy lock acquired, while the newly assigned
351// callback has to conform to these same restrictions. Or
352// (2) run without proxy lock acquired (e.g., being posted to a MessageLoop
353// and run there). The callback must be destroyed on the same thread where it
354// was run (but can be destroyed with or without the proxy lock acquired). Or
355// (3) destroyed without the proxy lock acquired.
356template <class FunctionType>
357inline base::Callback<FunctionType> RunWhileLocked(
358    const base::Callback<FunctionType>& callback) {
359  internal::RunWhileLockedHelper<FunctionType>* helper =
360      new internal::RunWhileLockedHelper<FunctionType>(callback);
361  return base::Bind(
362      &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
363      base::Owned(helper));
364}
365
366}  // namespace ppapi
367
368#endif  // PPAPI_SHARED_IMPL_PROXY_LOCK_H_
369