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" |
| 12 #include "ppapi/c/dev/ppb_message_loop_dev.h" |
11 #include "ppapi/c/pp_completion_callback.h" | 13 #include "ppapi/c/pp_completion_callback.h" |
12 #include "ppapi/c/pp_errors.h" | 14 #include "ppapi/c/pp_errors.h" |
13 #include "ppapi/shared_impl/callback_tracker.h" | 15 #include "ppapi/shared_impl/callback_tracker.h" |
14 #include "ppapi/shared_impl/ppapi_globals.h" | 16 #include "ppapi/shared_impl/ppapi_globals.h" |
15 #include "ppapi/shared_impl/proxy_lock.h" | 17 #include "ppapi/shared_impl/proxy_lock.h" |
16 #include "ppapi/shared_impl/resource.h" | 18 #include "ppapi/shared_impl/resource.h" |
17 | 19 |
18 namespace ppapi { | 20 namespace ppapi { |
19 | 21 |
20 // TrackedCallback ------------------------------------------------------------- | 22 // TrackedCallback ------------------------------------------------------------- |
21 | 23 |
22 // 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. |
23 TrackedCallback::TrackedCallback( | 25 TrackedCallback::TrackedCallback( |
24 Resource* resource, | 26 Resource* resource, |
25 const PP_CompletionCallback& callback) | 27 const PP_CompletionCallback& callback) |
26 : ALLOW_THIS_IN_INITIALIZER_LIST(abort_impl_factory_(this)), | 28 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), |
27 resource_id_(resource->pp_resource()), | 29 resource_id_(resource ? resource->pp_resource() : 0), |
28 completed_(false), | 30 completed_(false), |
29 aborted_(false), | 31 aborted_(false), |
30 callback_(callback) { | 32 callback_(callback), |
31 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( | 33 result_for_blocked_callback_(PP_OK) { |
32 resource->pp_instance()), | 34 // We can only track this callback if the resource is valid. It can be NULL |
33 tracker_->Add(make_scoped_refptr(this)); | 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 |
| 38 // have an instance (e.g. for MouseLock). |
| 39 if (resource) { |
| 40 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( |
| 41 resource->pp_instance()); |
| 42 tracker_->Add(make_scoped_refptr(this)); |
| 43 } |
| 44 |
| 45 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); |
| 46 // We only need a ConditionVariable if the lock is valid (i.e., we're out-of- |
| 47 // process) and the callback is blocking. |
| 48 if (proxy_lock && is_blocking()) |
| 49 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock)); |
34 } | 50 } |
35 | 51 |
36 TrackedCallback::~TrackedCallback() { | 52 TrackedCallback::~TrackedCallback() { |
37 } | 53 } |
38 | 54 |
39 void TrackedCallback::Abort() { | 55 void TrackedCallback::Abort() { |
40 if (!completed()) { | 56 if (!completed()) { |
41 aborted_ = true; | 57 aborted_ = true; |
42 Run(PP_ERROR_ABORTED); | 58 Run(PP_ERROR_ABORTED); |
43 } | 59 } |
44 } | 60 } |
45 | 61 |
46 void TrackedCallback::PostAbort() { | 62 void TrackedCallback::PostAbort() { |
47 if (!completed()) { | 63 // It doesn't make sense to abort a callback that's not associated with a |
| 64 // resource. |
| 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_) { |
48 aborted_ = true; | 70 aborted_ = true; |
49 // Post a task for the abort (only if necessary). | 71 MessageLoop::current()->PostTask( |
50 if (!abort_impl_factory_.HasWeakPtrs()) { | 72 FROM_HERE, |
51 MessageLoop::current()->PostTask( | 73 RunWhileLocked(base::Bind(&TrackedCallback::Abort, |
52 FROM_HERE, | 74 weak_ptr_factory_.GetWeakPtr()))); |
53 RunWhileLocked(base::Bind(&TrackedCallback::Abort, | |
54 abort_impl_factory_.GetWeakPtr()))); | |
55 } | |
56 } | 75 } |
57 } | 76 } |
58 | 77 |
59 void TrackedCallback::Run(int32_t result) { | 78 void TrackedCallback::Run(int32_t result) { |
60 if (!completed()) { | 79 if (!completed()) { |
61 // Cancel any pending calls. | 80 // Cancel any pending calls. |
62 abort_impl_factory_.InvalidateWeakPtrs(); | 81 weak_ptr_factory_.InvalidateWeakPtrs(); |
63 | 82 |
64 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| | 83 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| |
65 // may delete us. | 84 // may delete us. |
66 PP_CompletionCallback callback = callback_; | 85 PP_CompletionCallback callback = callback_; |
67 if (aborted()) | 86 if (aborted()) |
68 result = PP_ERROR_ABORTED; | 87 result = PP_ERROR_ABORTED; |
69 | 88 |
70 // Do this before running the callback in case of reentrancy (which | 89 if (is_blocking()) { |
71 // shouldn't happen, but avoid strange failures). | 90 // If the condition variable is invalid, there are two possibilities. One, |
72 MarkAsCompleted(); | 91 // we're running in-process, in which case the call should have come in on |
73 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); | 92 // 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 |
| 94 // blocking callback. Either way, there's some internal error. |
| 95 if (!operation_completed_condvar_.get()) { |
| 96 NOTREACHED(); |
| 97 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 } |
74 } | 116 } |
75 } | 117 } |
76 | 118 |
| 119 void TrackedCallback::PostRun(int32_t result) { |
| 120 DCHECK(!completed()); |
| 121 if (!completed()) { |
| 122 // There should be no pending calls. |
| 123 DCHECK(!weak_ptr_factory_.HasWeakPtrs()); |
| 124 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 125 |
| 126 if (resource_id_) { |
| 127 // If it has a resource_id_, it's in the tracker, and may be aborted if |
| 128 // the resource is destroyed. |
| 129 MessageLoop::current()->PostTask( |
| 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 } |
| 148 |
77 // static | 149 // static |
78 bool TrackedCallback::IsPending( | 150 bool TrackedCallback::IsPending( |
79 const scoped_refptr<TrackedCallback>& callback) { | 151 const scoped_refptr<TrackedCallback>& callback) { |
80 if (!callback.get()) | 152 if (!callback.get()) |
81 return false; | 153 return false; |
82 return !callback->completed(); | 154 return !callback->completed(); |
83 } | 155 } |
84 | 156 |
85 // static | 157 // static |
86 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback, | 158 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback, |
87 int32_t result) { | 159 int32_t result) { |
88 scoped_refptr<TrackedCallback> temp; | 160 scoped_refptr<TrackedCallback> temp; |
89 temp.swap(*callback); | 161 temp.swap(*callback); |
90 temp->Run(result); | 162 temp->Run(result); |
91 } | 163 } |
92 | 164 |
93 // static | 165 // static |
94 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) { | 166 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) { |
95 scoped_refptr<TrackedCallback> temp; | 167 scoped_refptr<TrackedCallback> temp; |
96 temp.swap(*callback); | 168 temp.swap(*callback); |
97 temp->Abort(); | 169 temp->Abort(); |
98 } | 170 } |
99 | 171 |
| 172 int32_t TrackedCallback::BlockUntilComplete() { |
| 173 // 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). |
| 175 |
| 176 // It doesn't make sense to wait on a non-blocking callback. Furthermore, |
| 177 // BlockUntilComplete should never be called for in-process plugins, where |
| 178 // blocking callbacks are not supported. |
| 179 CHECK(operation_completed_condvar_.get()); |
| 180 if (!is_blocking() || !operation_completed_condvar_.get()) { |
| 181 NOTREACHED(); |
| 182 return PP_ERROR_FAILED; |
| 183 } |
| 184 |
| 185 while (!completed()) |
| 186 operation_completed_condvar_->Wait(); |
| 187 return result_for_blocked_callback_; |
| 188 } |
| 189 |
100 void TrackedCallback::MarkAsCompleted() { | 190 void TrackedCallback::MarkAsCompleted() { |
101 DCHECK(!completed()); | 191 DCHECK(!completed()); |
102 | 192 |
103 // We will be removed; maintain a reference to ensure we won't be deleted | 193 // We will be removed; maintain a reference to ensure we won't be deleted |
104 // until we're done. | 194 // until we're done. |
105 scoped_refptr<TrackedCallback> thiz = this; | 195 scoped_refptr<TrackedCallback> thiz = this; |
106 completed_ = true; | 196 completed_ = true; |
107 tracker_->Remove(thiz); | 197 // We may not have a valid resource, in which case we're not in the tracker. |
| 198 if (resource_id_) |
| 199 tracker_->Remove(thiz); |
108 tracker_ = NULL; | 200 tracker_ = NULL; |
109 } | 201 } |
110 | 202 |
111 } // namespace ppapi | 203 } // namespace ppapi |
OLD | NEW |