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 callbacks which aren't in |
| 36 // the tracker will generally be short-lived. |
| 37 // TODO(dmichael): Add tracking at the instance level, for callbacks that only |
| 38 // have an instance. |
| 39 if (resource) { |
| 40 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( |
| 41 resource->pp_instance()); |
| 42 tracker_->Add(make_scoped_refptr(this)); |
| 43 } |
| 44 |
| 45 if (base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock()) |
| 46 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock)); |
34 } | 47 } |
35 | 48 |
36 TrackedCallback::~TrackedCallback() { | 49 TrackedCallback::~TrackedCallback() { |
37 } | 50 } |
38 | 51 |
39 void TrackedCallback::Abort() { | 52 void TrackedCallback::Abort() { |
40 if (!completed()) { | 53 if (!completed()) { |
41 aborted_ = true; | 54 aborted_ = true; |
42 Run(PP_ERROR_ABORTED); | 55 Run(PP_ERROR_ABORTED); |
43 } | 56 } |
44 } | 57 } |
45 | 58 |
46 void TrackedCallback::PostAbort() { | 59 void TrackedCallback::PostAbort() { |
47 if (!completed()) { | 60 // It doesn't make sense to abort a callback that's not associated with a |
| 61 // resource. |
| 62 // TODO(dmichael): If we allow associating with an instance instead, we must |
| 63 // allow for aborts in the case of the instance being destroyed. |
| 64 DCHECK(resource_id_); |
| 65 |
| 66 if (!completed() && !aborted_) { |
48 aborted_ = true; | 67 aborted_ = true; |
49 // Post a task for the abort (only if necessary). | 68 MessageLoop::current()->PostTask( |
50 if (!abort_impl_factory_.HasWeakPtrs()) { | 69 FROM_HERE, |
51 MessageLoop::current()->PostTask( | 70 RunWhileLocked(base::Bind(&TrackedCallback::Abort, |
52 FROM_HERE, | 71 weak_ptr_factory_.GetWeakPtr()))); |
53 RunWhileLocked(base::Bind(&TrackedCallback::Abort, | |
54 abort_impl_factory_.GetWeakPtr()))); | |
55 } | |
56 } | 72 } |
57 } | 73 } |
58 | 74 |
59 void TrackedCallback::Run(int32_t result) { | 75 void TrackedCallback::Run(int32_t result) { |
60 if (!completed()) { | 76 if (!completed()) { |
61 // Cancel any pending calls. | 77 // Cancel any pending calls. |
62 abort_impl_factory_.InvalidateWeakPtrs(); | 78 weak_ptr_factory_.InvalidateWeakPtrs(); |
63 | 79 |
64 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| | 80 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| |
65 // may delete us. | 81 // may delete us. |
66 PP_CompletionCallback callback = callback_; | 82 PP_CompletionCallback callback = callback_; |
67 if (aborted()) | 83 if (aborted()) |
68 result = PP_ERROR_ABORTED; | 84 result = PP_ERROR_ABORTED; |
69 | 85 |
70 // Do this before running the callback in case of reentrancy (which | 86 if (is_blocking()) { |
71 // shouldn't happen, but avoid strange failures). | 87 // If the condition variable is invalid, we're probably running |
72 MarkAsCompleted(); | 88 // in-process, and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD |
73 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); | 89 // well before this. |
| 90 DCHECK(operation_completed_condvar_.get()); |
| 91 result_for_blocked_callback_ = result; |
| 92 // Retain ourselves, since MarkAsCompleted will remove us from the |
| 93 // tracker. Then MarkAsCompleted before waking up the blocked thread, |
| 94 // which could potentially re-enter. |
| 95 scoped_refptr<TrackedCallback> thiz(this); |
| 96 MarkAsCompleted(); |
| 97 // Wake up the blocked thread. See BlockUntilComplete for where the thread |
| 98 // Wait()s. |
| 99 operation_completed_condvar_->Signal(); |
| 100 } else { |
| 101 // Do this before running the callback in case of reentrancy (which |
| 102 // shouldn't happen, but avoid strange failures). |
| 103 MarkAsCompleted(); |
| 104 // TODO(dmichael): Associate a message loop with the callback; if it's not |
| 105 // the same as the current thread's loop, then post it to the right loop. |
| 106 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); |
| 107 } |
74 } | 108 } |
75 } | 109 } |
76 | 110 |
| 111 void TrackedCallback::PostRun(int32_t result) { |
| 112 DCHECK(!completed()); |
| 113 if (!completed()) { |
| 114 // There should be no pending calls. |
| 115 DCHECK(!weak_ptr_factory_.HasWeakPtrs()); |
| 116 weak_ptr_factory_.InvalidateWeakPtrs(); |
| 117 |
| 118 if (resource_id_) { |
| 119 // If it has a resource_id_, it's in the tracker, and may be aborted if |
| 120 // the resource is destroyed. |
| 121 MessageLoop::current()->PostTask( |
| 122 FROM_HERE, |
| 123 RunWhileLocked(base::Bind(&TrackedCallback::Run, |
| 124 weak_ptr_factory_.GetWeakPtr(), |
| 125 result))); |
| 126 } else { |
| 127 // There is no resource_id_ associated with this callback, so it can't be |
| 128 // aborted. We have the message loop retain the callback to make sure it |
| 129 // gets run. This can happen when EnterBase is given an invalid resource, |
| 130 // and in that case no resource or instance will retain this |
| 131 // TrackedCallback. |
| 132 MessageLoop::current()->PostTask( |
| 133 FROM_HERE, |
| 134 RunWhileLocked(base::Bind(&TrackedCallback::Run, |
| 135 this, |
| 136 result))); |
| 137 } |
| 138 } |
| 139 } |
| 140 |
77 // static | 141 // static |
78 bool TrackedCallback::IsPending( | 142 bool TrackedCallback::IsPending( |
79 const scoped_refptr<TrackedCallback>& callback) { | 143 const scoped_refptr<TrackedCallback>& callback) { |
80 if (!callback.get()) | 144 if (!callback.get()) |
81 return false; | 145 return false; |
82 return !callback->completed(); | 146 return !callback->completed(); |
83 } | 147 } |
84 | 148 |
85 // static | 149 // static |
86 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback, | 150 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback, |
87 int32_t result) { | 151 int32_t result) { |
88 scoped_refptr<TrackedCallback> temp; | 152 scoped_refptr<TrackedCallback> temp; |
89 temp.swap(*callback); | 153 temp.swap(*callback); |
90 temp->Run(result); | 154 temp->Run(result); |
91 } | 155 } |
92 | 156 |
93 // static | 157 // static |
94 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) { | 158 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) { |
95 scoped_refptr<TrackedCallback> temp; | 159 scoped_refptr<TrackedCallback> temp; |
96 temp.swap(*callback); | 160 temp.swap(*callback); |
97 temp->Abort(); | 161 temp->Abort(); |
98 } | 162 } |
99 | 163 |
| 164 int32_t TrackedCallback::BlockUntilComplete() { |
| 165 // Note, we are already holding the proxy lock in all these methods, including |
| 166 // this one (see ppapi/thunk/enter.cc for where it gets acquired). |
| 167 |
| 168 // It doesn't make sense to wait on a non-blocking callback. |
| 169 DCHECK(is_blocking()); |
| 170 // BlockUntilComplete should never be called for in-process plugins, where |
| 171 // blocking callbacks are not supported. |
| 172 DCHECK(operation_completed_condvar_.get()); |
| 173 |
| 174 while (!completed()) |
| 175 operation_completed_condvar_->Wait(); |
| 176 return result_for_blocked_callback_; |
| 177 } |
| 178 |
100 void TrackedCallback::MarkAsCompleted() { | 179 void TrackedCallback::MarkAsCompleted() { |
101 DCHECK(!completed()); | 180 DCHECK(!completed()); |
102 | 181 |
103 // We will be removed; maintain a reference to ensure we won't be deleted | 182 // We will be removed; maintain a reference to ensure we won't be deleted |
104 // until we're done. | 183 // until we're done. |
105 scoped_refptr<TrackedCallback> thiz = this; | 184 scoped_refptr<TrackedCallback> thiz = this; |
106 completed_ = true; | 185 completed_ = true; |
107 tracker_->Remove(thiz); | 186 // We may not have a valid resource, in which case we're not in the tracker. |
| 187 if (resource_id_) |
| 188 tracker_->Remove(thiz); |
108 tracker_ = NULL; | 189 tracker_ = NULL; |
109 } | 190 } |
110 | 191 |
111 } // namespace ppapi | 192 } // namespace ppapi |
OLD | NEW |