proxy_lock.h revision 58e6fbe4ee35d65e14b626c557d37565bf8ad179
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 ppapi {
20
21// 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
23// is normally accomplished by using an appropriate Enter RAII object at the
24// beginning of each thunk function.
25//
26// 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
28// tracker, etc.
29class PPAPI_SHARED_EXPORT ProxyLock {
30 public:
31  // 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,
33  // 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-
35  // process plugin case.
36  static void Acquire();
37  // Relinquish the proxy lock. If the lock has not been set, this does nothing.
38  static void Release();
39
40  // 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).
42  static void AssertAcquired();
43
44 private:
45  DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock);
46};
47
48// 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,
50// such as PPB_Var and PPB_Core.
51class ProxyAutoLock {
52 public:
53  ProxyAutoLock() {
54    ProxyLock::Acquire();
55  }
56  ~ProxyAutoLock() {
57    ProxyLock::Release();
58  }
59 private:
60  DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock);
61};
62
63// The inverse of the above; unlock on construction, lock on destruction. This
64// is useful for calling out to the plugin, when we need to unlock but ensure
65// that we re-acquire the lock when the plugin is returns or raises an
66// exception.
67class ProxyAutoUnlock {
68 public:
69  ProxyAutoUnlock() {
70    ProxyLock::Release();
71  }
72  ~ProxyAutoUnlock() {
73    ProxyLock::Acquire();
74  }
75 private:
76  DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock);
77};
78
79// A set of function template overloads for invoking a function pointer while
80// the ProxyLock is unlocked. This assumes that the luck is held.
81// CallWhileUnlocked unlocks the ProxyLock just before invoking the given
82// function. The lock is immediately re-acquired when the invoked function
83// function returns. CallWhileUnlocked returns whatever the given function
84// returned.
85//
86// Example usage:
87//   *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent,
88//                               instance,
89//                               resource->pp_resource());
90template <class ReturnType>
91ReturnType CallWhileUnlocked(ReturnType (*function)()) {
92  ProxyAutoUnlock unlock;
93  return function();
94}
95template <class ReturnType, class P1>
96ReturnType CallWhileUnlocked(ReturnType (*function)(P1), const P1& p1) {
97  ProxyAutoUnlock unlock;
98  return function(p1);
99}
100template <class ReturnType, class P1, class P2>
101ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2),
102                             const P1& p1,
103                             const P2& p2) {
104  ProxyAutoUnlock unlock;
105  return function(p1, p2);
106}
107template <class ReturnType, class P1, class P2, class P3>
108ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3),
109                             const P1& p1,
110                             const P2& p2,
111                             const P3& p3) {
112  ProxyAutoUnlock unlock;
113  return function(p1, p2, p3);
114}
115template <class ReturnType, class P1, class P2, class P3, class P4>
116ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4),
117                             const P1& p1,
118                             const P2& p2,
119                             const P3& p3,
120                             const P4& p4) {
121  ProxyAutoUnlock unlock;
122  return function(p1, p2, p3, p4);
123}
124template <class ReturnType, class P1, class P2, class P3, class P4, class P5>
125ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4, P5),
126                             const P1& p1,
127                             const P2& p2,
128                             const P3& p3,
129                             const P4& p4,
130                             const P5& p5) {
131  ProxyAutoUnlock unlock;
132  return function(p1, p2, p3, p4, p5);
133}
134void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure);
135
136namespace internal {
137
138template <typename RunType>
139class RunWhileLockedHelper;
140
141template <>
142class RunWhileLockedHelper<void ()> {
143 public:
144  typedef base::Callback<void ()> CallbackType;
145  explicit RunWhileLockedHelper(const CallbackType& callback)
146      : callback_(new CallbackType(callback)) {
147    // Copying |callback| may adjust reference counts for bound Vars or
148    // Resources; we should have the lock already.
149    ProxyLock::AssertAcquired();
150    // CallWhileLocked and destruction might happen on a different thread from
151    // creation.
152    thread_checker_.DetachFromThread();
153  }
154  void CallWhileLocked() {
155    // Bind thread_checker_ to this thread so we can check in the destructor.
156    DCHECK(thread_checker_.CalledOnValidThread());
157    ProxyAutoLock lock;
158    {
159      // 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()
161      // throws an exception.
162      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
163      temp_callback->Run();
164    }
165  }
166
167 private:
168  scoped_ptr<CallbackType> callback_;
169
170  // Used to ensure that the Callback is run and deleted on the same thread.
171  base::ThreadChecker thread_checker_;
172};
173
174template <typename P1>
175class RunWhileLockedHelper<void (P1)> {
176 public:
177  typedef base::Callback<void (P1)> CallbackType;
178  explicit RunWhileLockedHelper(const CallbackType& callback)
179      : callback_(new CallbackType(callback)) {
180    ProxyLock::AssertAcquired();
181    thread_checker_.DetachFromThread();
182  }
183  void CallWhileLocked(P1 p1) {
184    DCHECK(thread_checker_.CalledOnValidThread());
185    ProxyAutoLock lock;
186    {
187      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
188      temp_callback->Run(p1);
189    }
190  }
191
192 private:
193  scoped_ptr<CallbackType> callback_;
194  base::ThreadChecker thread_checker_;
195};
196
197template <typename P1, typename P2>
198class RunWhileLockedHelper<void (P1, P2)> {
199 public:
200  typedef base::Callback<void (P1, P2)> CallbackType;
201  explicit RunWhileLockedHelper(const CallbackType& callback)
202      : callback_(new CallbackType(callback)) {
203    ProxyLock::AssertAcquired();
204    thread_checker_.DetachFromThread();
205  }
206  void CallWhileLocked(P1 p1, P2 p2) {
207    DCHECK(thread_checker_.CalledOnValidThread());
208    ProxyAutoLock lock;
209    {
210      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
211      temp_callback->Run(p1, p2);
212    }
213  }
214
215 private:
216  scoped_ptr<CallbackType> callback_;
217  base::ThreadChecker thread_checker_;
218};
219
220template <typename P1, typename P2, typename P3>
221class RunWhileLockedHelper<void (P1, P2, P3)> {
222 public:
223  typedef base::Callback<void (P1, P2, P3)> CallbackType;
224  explicit RunWhileLockedHelper(const CallbackType& callback)
225      : callback_(new CallbackType(callback)) {
226    ProxyLock::AssertAcquired();
227    thread_checker_.DetachFromThread();
228  }
229  void CallWhileLocked(P1 p1, P2 p2, P3 p3) {
230    DCHECK(thread_checker_.CalledOnValidThread());
231    ProxyAutoLock lock;
232    {
233      scoped_ptr<CallbackType> temp_callback(callback_.Pass());
234      temp_callback->Run(p1, p2, p3);
235    }
236  }
237
238 private:
239  scoped_ptr<CallbackType> callback_;
240  base::ThreadChecker thread_checker_;
241};
242
243}  // namespace internal
244
245// RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
246//  1) Locks the ProxyLock.
247//  2) Runs the original Callback (forwarding arguments, if any).
248//  3) Clears the original Callback (while the lock is held).
249//  4) Unlocks the ProxyLock.
250// Note that it's important that the callback is cleared in step (3), in case
251// clearing the Callback causes a destructor (e.g., for a Resource) to run,
252// which should hold the ProxyLock to avoid data races.
253//
254// This is for cases where you want to run a task or store a Callback, but you
255// want to ensure that the ProxyLock is acquired for the duration of the task
256// that the Callback runs.
257// EXAMPLE USAGE:
258//   GetMainThreadMessageLoop()->PostDelayedTask(
259//     FROM_HERE,
260//     RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)),
261//     delay_in_ms);
262//
263// 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
265// 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
267// destroyed with or without the proxy lock acquired, while the newly assigned
268// callback has to conform to these same restrictions. Or
269// (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
271// was run (but can be destroyed with or without the proxy lock acquired). Or
272// (3) destroyed without the proxy lock acquired.
273// TODO(dmichael): This won't actually fail until
274//                 https://codereview.chromium.org/19492014/ lands.
275template <class FunctionType>
276inline base::Callback<FunctionType>
277RunWhileLocked(const base::Callback<FunctionType>& callback) {
278  internal::RunWhileLockedHelper<FunctionType>* helper =
279      new internal::RunWhileLockedHelper<FunctionType>(callback);
280  return base::Bind(
281      &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
282      base::Owned(helper));
283}
284
285}  // namespace ppapi
286
287#endif  // PPAPI_SHARED_IMPL_PROXY_LOCK_H_
288