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/ppb_message_loop_shared.h" |
17 #include "ppapi/shared_impl/proxy_lock.h" | 18 #include "ppapi/shared_impl/proxy_lock.h" |
18 #include "ppapi/shared_impl/resource.h" | 19 #include "ppapi/shared_impl/resource.h" |
19 | 20 |
20 namespace ppapi { | 21 namespace ppapi { |
21 | 22 |
| 23 namespace { |
| 24 |
| 25 bool IsMainThread() { |
| 26 return |
| 27 PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread(); |
| 28 } |
| 29 |
| 30 } // namespace |
| 31 |
22 // TrackedCallback ------------------------------------------------------------- | 32 // TrackedCallback ------------------------------------------------------------- |
23 | 33 |
24 // Note: don't keep a Resource* since it may go out of scope before us. | 34 // Note: don't keep a Resource* since it may go out of scope before us. |
25 TrackedCallback::TrackedCallback( | 35 TrackedCallback::TrackedCallback( |
26 Resource* resource, | 36 Resource* resource, |
27 const PP_CompletionCallback& callback) | 37 const PP_CompletionCallback& callback) |
28 : is_scheduled_(false), | 38 : is_scheduled_(false), |
29 resource_id_(resource ? resource->pp_resource() : 0), | 39 resource_id_(resource ? resource->pp_resource() : 0), |
30 completed_(false), | 40 completed_(false), |
31 aborted_(false), | 41 aborted_(false), |
32 callback_(callback), | 42 callback_(callback), |
| 43 target_loop_(PpapiGlobals::Get()->GetCurrentMessageLoop()), |
33 result_for_blocked_callback_(PP_OK) { | 44 result_for_blocked_callback_(PP_OK) { |
| 45 // Note that target_loop_ may be NULL at this point, if the plugin has not |
| 46 // attached a loop to this thread, or if this is an in-process plugin. |
| 47 // The Enter class should handle checking this for us. |
| 48 |
34 // TODO(dmichael): Add tracking at the instance level, for callbacks that only | 49 // TODO(dmichael): Add tracking at the instance level, for callbacks that only |
35 // have an instance (e.g. for MouseLock). | 50 // have an instance (e.g. for MouseLock). |
36 if (resource) { | 51 if (resource) { |
37 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( | 52 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( |
38 resource->pp_instance()); | 53 resource->pp_instance()); |
39 tracker_->Add(make_scoped_refptr(this)); | 54 tracker_->Add(make_scoped_refptr(this)); |
40 } | 55 } |
41 | 56 |
42 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); | 57 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); |
43 // We only need a ConditionVariable if the lock is valid (i.e., we're out-of- | 58 if (proxy_lock) { |
44 // process) and the callback is blocking. | 59 // If the proxy_lock is valid, we're running out-of-process, and locking |
45 if (proxy_lock && is_blocking()) | 60 // is enabled. |
46 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock)); | 61 if (is_blocking()) { |
| 62 // This is a blocking completion callback, so we will need a condition |
| 63 // variable for blocking & signalling the calling thread. |
| 64 operation_completed_condvar_.reset( |
| 65 new base::ConditionVariable(proxy_lock)); |
| 66 } else { |
| 67 // It's a non-blocking callback, so we should have a MessageLoopResource |
| 68 // to dispatch to. Note that we don't error check here, though. Later, |
| 69 // EnterResource::SetResult will check to make sure the callback is valid |
| 70 // and take appropriate action. |
| 71 } |
| 72 } |
47 } | 73 } |
48 | 74 |
49 TrackedCallback::~TrackedCallback() { | 75 TrackedCallback::~TrackedCallback() { |
50 } | 76 } |
51 | 77 |
52 void TrackedCallback::Abort() { | 78 void TrackedCallback::Abort() { |
53 // It doesn't make sense to abort a callback that's not associated with a | |
54 // resource. | |
55 DCHECK(resource_id_); | |
56 Run(PP_ERROR_ABORTED); | 79 Run(PP_ERROR_ABORTED); |
57 } | 80 } |
58 | 81 |
59 void TrackedCallback::PostAbort() { | 82 void TrackedCallback::PostAbort() { |
60 PostRun(PP_ERROR_ABORTED); | 83 PostRun(PP_ERROR_ABORTED); |
61 } | 84 } |
62 | 85 |
63 void TrackedCallback::Run(int32_t result) { | 86 void TrackedCallback::Run(int32_t result) { |
64 // Only allow the callback to be run once. Note that this also covers the case | 87 // Only allow the callback to be run once. Note that this also covers the case |
65 // where the callback was previously Aborted because its associated Resource | 88 // where the callback was previously Aborted because its associated Resource |
66 // went away. The callback may live on for a while because of a reference from | 89 // 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 | 90 // a Closure. But when the Closure runs, Run() quietly does nothing, and the |
68 // callback will go away when all referring Closures go away. | 91 // callback will go away when all referring Closures go away. |
69 if (completed()) | 92 if (completed()) |
70 return; | 93 return; |
71 if (result == PP_ERROR_ABORTED) | 94 if (result == PP_ERROR_ABORTED) |
72 aborted_ = true; | 95 aborted_ = true; |
73 | 96 |
74 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| | |
75 // may delete us. | |
76 PP_CompletionCallback callback = callback_; | |
77 // Note that this call of Run() may have been scheduled prior to Abort() or | 97 // Note that this call of Run() may have been scheduled prior to Abort() or |
78 // PostAbort() being called. If we have been told to Abort, that always | 98 // PostAbort() being called. If we have been told to Abort, that always |
79 // trumps a result that was scheduled before. | 99 // trumps a result that was scheduled before, so we should make sure to pass |
| 100 // PP_ERROR_ABORTED. |
80 if (aborted()) | 101 if (aborted()) |
81 result = PP_ERROR_ABORTED; | 102 result = PP_ERROR_ABORTED; |
82 | 103 |
83 if (is_blocking()) { | 104 if (is_blocking()) { |
84 // If the condition variable is invalid, there are two possibilities. One, | 105 // If the condition variable is invalid, there are two possibilities. One, |
85 // we're running in-process, in which case the call should have come in on | 106 // we're running in-process, in which case the call should have come in on |
86 // the main thread and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD | 107 // the main thread and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD |
87 // well before this. Otherwise, this callback was not created as a | 108 // well before this. Otherwise, this callback was not created as a |
88 // blocking callback. Either way, there's some internal error. | 109 // blocking callback. Either way, there's some internal error. |
89 if (!operation_completed_condvar_.get()) { | 110 if (!operation_completed_condvar_.get()) { |
90 NOTREACHED(); | 111 NOTREACHED(); |
91 return; | 112 return; |
92 } | 113 } |
93 result_for_blocked_callback_ = result; | 114 result_for_blocked_callback_ = result; |
94 // Retain ourselves, since MarkAsCompleted will remove us from the | 115 // Retain ourselves, since MarkAsCompleted will remove us from the |
95 // tracker. Then MarkAsCompleted before waking up the blocked thread, | 116 // tracker. Then MarkAsCompleted before waking up the blocked thread, |
96 // which could potentially re-enter. | 117 // which could potentially re-enter. |
97 scoped_refptr<TrackedCallback> thiz(this); | 118 scoped_refptr<TrackedCallback> thiz(this); |
98 MarkAsCompleted(); | 119 MarkAsCompleted(); |
99 // Wake up the blocked thread. See BlockUntilComplete for where the thread | 120 // Wake up the blocked thread. See BlockUntilComplete for where the thread |
100 // Wait()s. | 121 // Wait()s. |
101 operation_completed_condvar_->Signal(); | 122 operation_completed_condvar_->Signal(); |
102 } else { | 123 } else { |
| 124 // If there's a target_loop_, and we're not on the right thread, we need to |
| 125 // post to target_loop_. |
| 126 if (target_loop_ && |
| 127 target_loop_ != PpapiGlobals::Get()->GetCurrentMessageLoop()) { |
| 128 PostRun(result); |
| 129 return; |
| 130 } |
| 131 // Copy |callback_| now, since |MarkAsCompleted()| may delete us. |
| 132 PP_CompletionCallback callback = callback_; |
103 // Do this before running the callback in case of reentrancy (which | 133 // Do this before running the callback in case of reentrancy (which |
104 // shouldn't happen, but avoid strange failures). | 134 // shouldn't happen, but avoid strange failures). |
105 MarkAsCompleted(); | 135 MarkAsCompleted(); |
106 // TODO(dmichael): Associate a message loop with the callback; if it's not | 136 // 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. | 137 // the same as the current thread's loop, then post it to the right loop. |
108 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); | 138 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); |
109 } | 139 } |
110 } | 140 } |
111 | 141 |
112 void TrackedCallback::PostRun(int32_t result) { | 142 void TrackedCallback::PostRun(int32_t result) { |
113 if (completed()) { | 143 if (completed()) { |
114 NOTREACHED(); | 144 NOTREACHED(); |
115 return; | 145 return; |
116 } | 146 } |
117 if (result == PP_ERROR_ABORTED) | 147 if (result == PP_ERROR_ABORTED) |
118 aborted_ = true; | 148 aborted_ = true; |
119 // We might abort when there's already a scheduled callback, but callers | 149 // We might abort when there's already a scheduled callback, but callers |
120 // should never try to PostRun more than once otherwise. | 150 // should never try to PostRun more than once otherwise. |
121 DCHECK(result == PP_ERROR_ABORTED || !is_scheduled_); | 151 DCHECK(result == PP_ERROR_ABORTED || !is_scheduled_); |
122 | 152 |
123 base::Closure callback_closure( | 153 base::Closure callback_closure( |
124 RunWhileLocked(base::Bind(&TrackedCallback::Run, this, result))); | 154 RunWhileLocked(base::Bind(&TrackedCallback::Run, this, result))); |
125 MessageLoop::current()->PostTask(FROM_HERE, callback_closure); | 155 if (!target_loop_) { |
| 156 // We must be running in-process and on the main thread (the Enter |
| 157 // classes protect against having a null target_loop_ otherwise). |
| 158 DCHECK(IsMainThread()); |
| 159 DCHECK(PpapiGlobals::Get()->IsHostGlobals()); |
| 160 MessageLoop::current()->PostTask(FROM_HERE, callback_closure); |
| 161 } else { |
| 162 target_loop_->PostClosure(FROM_HERE, callback_closure, 0); |
| 163 } |
126 is_scheduled_ = true; | 164 is_scheduled_ = true; |
127 } | 165 } |
128 | 166 |
129 // static | 167 // static |
130 bool TrackedCallback::IsPending( | 168 bool TrackedCallback::IsPending( |
131 const scoped_refptr<TrackedCallback>& callback) { | 169 const scoped_refptr<TrackedCallback>& callback) { |
132 if (!callback.get()) | 170 if (!callback.get()) |
133 return false; | 171 return false; |
134 return !callback->completed(); | 172 return !callback->completed(); |
135 } | 173 } |
(...skipping 23 matching lines...) Expand all Loading... |
159 // until we're done. | 197 // until we're done. |
160 scoped_refptr<TrackedCallback> thiz = this; | 198 scoped_refptr<TrackedCallback> thiz = this; |
161 completed_ = true; | 199 completed_ = true; |
162 // We may not have a valid resource, in which case we're not in the tracker. | 200 // We may not have a valid resource, in which case we're not in the tracker. |
163 if (resource_id_) | 201 if (resource_id_) |
164 tracker_->Remove(thiz); | 202 tracker_->Remove(thiz); |
165 tracker_ = NULL; | 203 tracker_ = NULL; |
166 } | 204 } |
167 | 205 |
168 } // namespace ppapi | 206 } // namespace ppapi |
OLD | NEW |