Chromium Code Reviews| 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 #include "ppapi/shared_impl/tracked_callback.h" | 5 #include "ppapi/shared_impl/tracked_callback.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/compiler_specific.h" | 8 #include "base/compiler_specific.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
| 11 #include "base/synchronization/lock.h" | 11 #include "base/synchronization/lock.h" |
| 12 #include "ppapi/c/dev/ppb_message_loop_dev.h" | 12 #include "ppapi/c/dev/ppb_message_loop_dev.h" |
| 13 #include "ppapi/c/pp_completion_callback.h" | 13 #include "ppapi/c/pp_completion_callback.h" |
| 14 #include "ppapi/c/pp_errors.h" | 14 #include "ppapi/c/pp_errors.h" |
| 15 #include "ppapi/shared_impl/callback_tracker.h" | 15 #include "ppapi/shared_impl/callback_tracker.h" |
| 16 #include "ppapi/shared_impl/ppapi_globals.h" | 16 #include "ppapi/shared_impl/ppapi_globals.h" |
| 17 #include "ppapi/shared_impl/proxy_lock.h" | 17 #include "ppapi/shared_impl/proxy_lock.h" |
| 18 #include "ppapi/shared_impl/resource.h" | 18 #include "ppapi/shared_impl/resource.h" |
| 19 | 19 |
| 20 namespace ppapi { | 20 namespace ppapi { |
| 21 | 21 |
| 22 // TrackedCallback ------------------------------------------------------------- | 22 // TrackedCallback ------------------------------------------------------------- |
| 23 | 23 |
| 24 // Note: don't keep a Resource* since it may go out of scope before us. | 24 // Note: don't keep a Resource* since it may go out of scope before us. |
| 25 TrackedCallback::TrackedCallback( | 25 TrackedCallback::TrackedCallback( |
| 26 Resource* resource, | 26 Resource* resource, |
| 27 const PP_CompletionCallback& callback) | 27 const PP_CompletionCallback& callback) |
| 28 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), | 28 : is_scheduled_(false), |
| 29 resource_id_(resource ? resource->pp_resource() : 0), | 29 resource_id_(resource ? resource->pp_resource() : 0), |
| 30 completed_(false), | 30 completed_(false), |
| 31 aborted_(false), | 31 aborted_(false), |
| 32 callback_(callback), | 32 callback_(callback), |
| 33 result_for_blocked_callback_(PP_OK) { | 33 result_for_blocked_callback_(PP_OK) { |
| 34 // We can only track this callback if the resource is valid. It can be NULL | |
| 35 // in error conditions in the Enter* classes and for callbacks associated with | |
| 36 // an instance. | |
| 37 // TODO(dmichael): Add tracking at the instance level, for callbacks that only | 34 // TODO(dmichael): Add tracking at the instance level, for callbacks that only |
| 38 // have an instance (e.g. for MouseLock). | 35 // have an instance (e.g. for MouseLock). |
| 39 if (resource) { | 36 if (resource) { |
| 40 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( | 37 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( |
| 41 resource->pp_instance()); | 38 resource->pp_instance()); |
| 42 tracker_->Add(make_scoped_refptr(this)); | 39 tracker_->Add(make_scoped_refptr(this)); |
| 43 } | 40 } |
| 44 | 41 |
| 45 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); | 42 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); |
| 46 // We only need a ConditionVariable if the lock is valid (i.e., we're out-of- | 43 // We only need a ConditionVariable if the lock is valid (i.e., we're out-of- |
| 47 // process) and the callback is blocking. | 44 // process) and the callback is blocking. |
| 48 if (proxy_lock && is_blocking()) | 45 if (proxy_lock && is_blocking()) |
| 49 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock)); | 46 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock)); |
| 50 } | 47 } |
| 51 | 48 |
| 52 TrackedCallback::~TrackedCallback() { | 49 TrackedCallback::~TrackedCallback() { |
| 53 } | 50 } |
| 54 | 51 |
| 55 void TrackedCallback::Abort() { | 52 void TrackedCallback::Abort() { |
| 56 if (!completed()) { | 53 // It doesn't make sense to abort a callback that's not associated with a |
| 57 aborted_ = true; | 54 // resource. |
| 58 Run(PP_ERROR_ABORTED); | 55 DCHECK(resource_id_); |
| 59 } | 56 Run(PP_ERROR_ABORTED); |
| 60 } | 57 } |
| 61 | 58 |
| 62 void TrackedCallback::PostAbort() { | 59 void TrackedCallback::PostAbort() { |
| 63 // It doesn't make sense to abort a callback that's not associated with a | 60 PostRun(PP_ERROR_ABORTED); |
| 64 // resource. | |
|
dmichael (off chromium)
2012/09/14 21:30:36
It turns out it does make sense... ppapi_plugin_i
| |
| 65 // TODO(dmichael): If we allow associating with an instance instead, we must | |
| 66 // allow for aborts in the case of the instance being destroyed. | |
| 67 DCHECK(resource_id_); | |
| 68 | |
| 69 if (!completed() && !aborted_) { | |
| 70 aborted_ = true; | |
| 71 MessageLoop::current()->PostTask( | |
| 72 FROM_HERE, | |
| 73 RunWhileLocked(base::Bind(&TrackedCallback::Abort, | |
| 74 weak_ptr_factory_.GetWeakPtr()))); | |
| 75 } | |
| 76 } | 61 } |
| 77 | 62 |
| 78 void TrackedCallback::Run(int32_t result) { | 63 void TrackedCallback::Run(int32_t result) { |
| 79 if (!completed()) { | 64 // Only allow the callback to be run once. Note that this also covers the case |
| 80 // Cancel any pending calls. | 65 // where the callback was previously Aborted because its associated Resource |
| 81 weak_ptr_factory_.InvalidateWeakPtrs(); | 66 // went away. The callback may live on for a while because of a reference from |
| 67 // a Closure. But when the Closure runs, Run() quietly does nothing, and the | |
| 68 // callback will go away when all referring Closures go away. | |
| 69 if (completed()) | |
| 70 return; | |
| 71 if (result == PP_ERROR_ABORTED) | |
| 72 aborted_ = true; | |
| 82 | 73 |
| 83 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| | 74 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| |
| 84 // may delete us. | 75 // may delete us. |
| 85 PP_CompletionCallback callback = callback_; | 76 PP_CompletionCallback callback = callback_; |
| 86 if (aborted()) | 77 // Note that this call of Run() may have been scheduled prior to Abort() or |
| 87 result = PP_ERROR_ABORTED; | 78 // PostAbort() being called. If we have been told to Abort, that always |
| 79 // trumps a result that was scheduled before. | |
| 80 if (aborted()) | |
| 81 result = PP_ERROR_ABORTED; | |
| 88 | 82 |
| 89 if (is_blocking()) { | 83 if (is_blocking()) { |
| 90 // If the condition variable is invalid, there are two possibilities. One, | 84 // If the condition variable is invalid, there are two possibilities. One, |
| 91 // we're running in-process, in which case the call should have come in on | 85 // we're running in-process, in which case the call should have come in on |
| 92 // the main thread and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD | 86 // the main thread and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD |
| 93 // well before this. Otherwise, this callback was not created as a | 87 // well before this. Otherwise, this callback was not created as a |
| 94 // blocking callback. Either way, there's some internal error. | 88 // blocking callback. Either way, there's some internal error. |
| 95 if (!operation_completed_condvar_.get()) { | 89 if (!operation_completed_condvar_.get()) { |
| 96 NOTREACHED(); | 90 NOTREACHED(); |
| 97 return; | 91 return; |
| 98 } | |
| 99 result_for_blocked_callback_ = result; | |
| 100 // Retain ourselves, since MarkAsCompleted will remove us from the | |
| 101 // tracker. Then MarkAsCompleted before waking up the blocked thread, | |
| 102 // which could potentially re-enter. | |
| 103 scoped_refptr<TrackedCallback> thiz(this); | |
| 104 MarkAsCompleted(); | |
| 105 // Wake up the blocked thread. See BlockUntilComplete for where the thread | |
| 106 // Wait()s. | |
| 107 operation_completed_condvar_->Signal(); | |
| 108 } else { | |
| 109 // Do this before running the callback in case of reentrancy (which | |
| 110 // shouldn't happen, but avoid strange failures). | |
| 111 MarkAsCompleted(); | |
| 112 // TODO(dmichael): Associate a message loop with the callback; if it's not | |
| 113 // the same as the current thread's loop, then post it to the right loop. | |
| 114 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); | |
| 115 } | 92 } |
| 93 result_for_blocked_callback_ = result; | |
| 94 // Retain ourselves, since MarkAsCompleted will remove us from the | |
| 95 // tracker. Then MarkAsCompleted before waking up the blocked thread, | |
| 96 // which could potentially re-enter. | |
| 97 scoped_refptr<TrackedCallback> thiz(this); | |
| 98 MarkAsCompleted(); | |
| 99 // Wake up the blocked thread. See BlockUntilComplete for where the thread | |
| 100 // Wait()s. | |
| 101 operation_completed_condvar_->Signal(); | |
| 102 } else { | |
| 103 // Do this before running the callback in case of reentrancy (which | |
| 104 // shouldn't happen, but avoid strange failures). | |
| 105 MarkAsCompleted(); | |
| 106 // TODO(dmichael): Associate a message loop with the callback; if it's not | |
| 107 // the same as the current thread's loop, then post it to the right loop. | |
| 108 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); | |
| 116 } | 109 } |
| 117 } | 110 } |
| 118 | 111 |
| 119 void TrackedCallback::PostRun(int32_t result) { | 112 void TrackedCallback::PostRun(int32_t result) { |
| 120 DCHECK(!completed()); | 113 if (completed()) { |
| 121 if (!completed()) { | 114 NOTREACHED(); |
| 122 // There should be no pending calls. | 115 return; |
| 123 DCHECK(!weak_ptr_factory_.HasWeakPtrs()); | 116 } |
| 124 weak_ptr_factory_.InvalidateWeakPtrs(); | 117 if (result == PP_ERROR_ABORTED) |
| 118 aborted_ = true; | |
| 119 // We might abort when there's already a scheduled callback, but callers | |
| 120 // should never try to PostRun more than once otherwise. | |
| 121 DCHECK(result == PP_ERROR_ABORTED || !is_scheduled_); | |
| 125 | 122 |
| 126 if (resource_id_) { | 123 base::Closure callback_closure( |
| 127 // If it has a resource_id_, it's in the tracker, and may be aborted if | 124 RunWhileLocked(base::Bind(&TrackedCallback::Run, this, result))); |
| 128 // the resource is destroyed. | 125 MessageLoop::current()->PostTask(FROM_HERE, callback_closure); |
| 129 MessageLoop::current()->PostTask( | 126 is_scheduled_ = true; |
| 130 FROM_HERE, | |
| 131 RunWhileLocked(base::Bind(&TrackedCallback::Run, | |
| 132 weak_ptr_factory_.GetWeakPtr(), | |
| 133 result))); | |
| 134 } else { | |
| 135 // There is no resource_id_ associated with this callback, so it can't be | |
| 136 // aborted. We have the message loop retain the callback to make sure it | |
| 137 // gets run. This can happen when EnterBase is given an invalid resource, | |
| 138 // and in that case no resource or instance will retain this | |
| 139 // TrackedCallback. | |
| 140 MessageLoop::current()->PostTask( | |
| 141 FROM_HERE, | |
| 142 RunWhileLocked(base::Bind(&TrackedCallback::Run, | |
| 143 this, | |
| 144 result))); | |
| 145 } | |
| 146 } | |
| 147 } | 127 } |
| 148 | 128 |
| 149 // static | 129 // static |
| 150 bool TrackedCallback::IsPending( | 130 bool TrackedCallback::IsPending( |
| 151 const scoped_refptr<TrackedCallback>& callback) { | 131 const scoped_refptr<TrackedCallback>& callback) { |
| 152 if (!callback.get()) | 132 if (!callback.get()) |
| 153 return false; | 133 return false; |
| 154 return !callback->completed(); | 134 return !callback->completed(); |
| 155 } | 135 } |
| 156 | 136 |
| 157 // static | |
| 158 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback, | |
| 159 int32_t result) { | |
| 160 scoped_refptr<TrackedCallback> temp; | |
| 161 temp.swap(*callback); | |
| 162 temp->Run(result); | |
| 163 } | |
| 164 | |
| 165 // static | |
| 166 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) { | |
| 167 scoped_refptr<TrackedCallback> temp; | |
| 168 temp.swap(*callback); | |
| 169 temp->Abort(); | |
| 170 } | |
| 171 | |
| 172 int32_t TrackedCallback::BlockUntilComplete() { | 137 int32_t TrackedCallback::BlockUntilComplete() { |
| 173 // Note, we are already holding the proxy lock in all these methods, including | 138 // Note, we are already holding the proxy lock in all these methods, including |
| 174 // this one (see ppapi/thunk/enter.cc for where it gets acquired). | 139 // this one (see ppapi/thunk/enter.cc for where it gets acquired). |
| 175 | 140 |
| 176 // It doesn't make sense to wait on a non-blocking callback. Furthermore, | 141 // It doesn't make sense to wait on a non-blocking callback. Furthermore, |
| 177 // BlockUntilComplete should never be called for in-process plugins, where | 142 // BlockUntilComplete should never be called for in-process plugins, where |
| 178 // blocking callbacks are not supported. | 143 // blocking callbacks are not supported. |
| 179 CHECK(operation_completed_condvar_.get()); | 144 CHECK(operation_completed_condvar_.get()); |
| 180 if (!is_blocking() || !operation_completed_condvar_.get()) { | 145 if (!is_blocking() || !operation_completed_condvar_.get()) { |
| 181 NOTREACHED(); | 146 NOTREACHED(); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 194 // until we're done. | 159 // until we're done. |
| 195 scoped_refptr<TrackedCallback> thiz = this; | 160 scoped_refptr<TrackedCallback> thiz = this; |
| 196 completed_ = true; | 161 completed_ = true; |
| 197 // We may not have a valid resource, in which case we're not in the tracker. | 162 // We may not have a valid resource, in which case we're not in the tracker. |
| 198 if (resource_id_) | 163 if (resource_id_) |
| 199 tracker_->Remove(thiz); | 164 tracker_->Remove(thiz); |
| 200 tracker_ = NULL; | 165 tracker_ = NULL; |
| 201 } | 166 } |
| 202 | 167 |
| 203 } // namespace ppapi | 168 } // namespace ppapi |
| OLD | NEW |