OLD | NEW |
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 | 12 |
12 #include "ppapi/shared_impl/ppapi_shared_export.h" | 13 #include "ppapi/shared_impl/ppapi_shared_export.h" |
13 | 14 |
14 namespace base { | 15 namespace base { |
15 class Lock; | 16 class Lock; |
16 } | 17 } |
17 | 18 |
18 namespace ppapi { | 19 namespace ppapi { |
19 | 20 |
20 // This is the one lock to rule them all for the ppapi proxy. All PPB interface | 21 // This is the one lock to rule them all for the ppapi proxy. All PPB interface |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 const P1& p1, | 126 const P1& p1, |
126 const P2& p2, | 127 const P2& p2, |
127 const P3& p3, | 128 const P3& p3, |
128 const P4& p4, | 129 const P4& p4, |
129 const P5& p5) { | 130 const P5& p5) { |
130 ProxyAutoUnlock unlock; | 131 ProxyAutoUnlock unlock; |
131 return function(p1, p2, p3, p4, p5); | 132 return function(p1, p2, p3, p4, p5); |
132 } | 133 } |
133 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure); | 134 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure); |
134 | 135 |
135 // CallWhileLocked locks the ProxyLock and runs the given closure immediately. | 136 namespace internal { |
136 // The lock is released when CallWhileLocked returns. This function assumes the | |
137 // lock is not held. This is mostly for use in RunWhileLocked; see below. | |
138 void PPAPI_SHARED_EXPORT CallWhileLocked(const base::Closure& closure); | |
139 | 137 |
140 // RunWhileLocked binds the given closure with CallWhileLocked and returns the | 138 template <typename RunType> |
141 // new Closure. This is for cases where you want to run a task, but you want to | 139 class RunWhileLockedHelper; |
142 // ensure that the ProxyLock is acquired for the duration of the task. | 140 |
143 // Example usage: | 141 template <> |
| 142 class 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 |
| 174 template <typename P1> |
| 175 class 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 |
| 197 template <typename P1, typename P2> |
| 198 class 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 |
| 220 template <typename P1, typename P2, typename P3> |
| 221 class 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: |
144 // GetMainThreadMessageLoop()->PostDelayedTask( | 258 // GetMainThreadMessageLoop()->PostDelayedTask( |
145 // FROM_HERE, | 259 // FROM_HERE, |
146 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)), | 260 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)), |
147 // delay_in_ms); | 261 // delay_in_ms); |
148 inline base::Closure RunWhileLocked(const base::Closure& closure) { | 262 // |
149 return base::Bind(CallWhileLocked, closure); | 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. |
| 275 template <class FunctionType> |
| 276 inline base::Callback<FunctionType> |
| 277 RunWhileLocked(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)); |
150 } | 283 } |
151 | 284 |
152 } // namespace ppapi | 285 } // namespace ppapi |
153 | 286 |
154 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ | 287 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ |
OLD | NEW |