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 |