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/thunk/enter.h" | 5 #include "ppapi/thunk/enter.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/stringprintf.h" | 10 #include "base/stringprintf.h" |
| 11 #include "base/synchronization/lock.h" |
11 #include "ppapi/c/pp_errors.h" | 12 #include "ppapi/c/pp_errors.h" |
12 #include "ppapi/shared_impl/ppapi_globals.h" | 13 #include "ppapi/shared_impl/ppapi_globals.h" |
| 14 #include "ppapi/shared_impl/tracked_callback.h" |
13 #include "ppapi/thunk/ppb_instance_api.h" | 15 #include "ppapi/thunk/ppb_instance_api.h" |
14 #include "ppapi/thunk/resource_creation_api.h" | 16 #include "ppapi/thunk/resource_creation_api.h" |
15 | 17 |
16 namespace ppapi { | 18 namespace ppapi { |
| 19 namespace { |
| 20 |
| 21 bool IsMainThread() { |
| 22 return |
| 23 PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread(); |
| 24 } |
| 25 |
| 26 } // namespace |
| 27 |
17 namespace thunk { | 28 namespace thunk { |
18 | 29 |
19 namespace subtle { | 30 namespace subtle { |
20 | 31 |
21 bool CallbackIsRequired(const PP_CompletionCallback& callback) { | 32 void AssertLockHeld() { |
22 return callback.func != NULL && | 33 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); |
23 (callback.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL) == 0; | 34 // The lock is only valid in the plugin side of the proxy, so it only makes |
| 35 // sense to assert there. Otherwise, silently succeed. |
| 36 if (proxy_lock) |
| 37 proxy_lock->AssertAcquired(); |
24 } | 38 } |
25 | 39 |
26 EnterBase::EnterBase() | 40 EnterBase::EnterBase() |
27 : callback_(PP_BlockUntilComplete()), | 41 : resource_(NULL), |
28 retval_(PP_OK) { | 42 retval_(PP_OK) { |
29 // TODO(brettw) validate threads. | 43 // TODO(dmichael) validate that threads have an associated message loop. |
30 } | 44 } |
31 | 45 |
32 EnterBase::EnterBase(const PP_CompletionCallback& callback) | 46 EnterBase::EnterBase(PP_Resource resource) |
33 : callback_(CallbackIsRequired(callback) ? callback | 47 : resource_(GetResource(resource)), |
34 : PP_BlockUntilComplete()), | |
35 retval_(PP_OK) { | 48 retval_(PP_OK) { |
36 // TODO(brettw) validate threads. | 49 // TODO(dmichael) validate that threads have an associated message loop. |
| 50 } |
| 51 |
| 52 EnterBase::EnterBase(PP_Resource resource, |
| 53 const PP_CompletionCallback& callback) |
| 54 : resource_(GetResource(resource)), |
| 55 retval_(PP_OK) { |
| 56 callback_ = new TrackedCallback(resource_, callback); |
| 57 |
| 58 // TODO(dmichael) validate that threads have an associated message loop. |
37 } | 59 } |
38 | 60 |
39 EnterBase::~EnterBase() { | 61 EnterBase::~EnterBase() { |
40 if (callback_.func) { | 62 // callback_ is cleared any time it is run, scheduled to be run, or once we |
41 // All async completions should have cleared the callback in SetResult(). | 63 // know it will be completed asynchronously. So by this point it should be |
42 DCHECK(retval_ != PP_OK_COMPLETIONPENDING && retval_ != PP_OK); | 64 // NULL. |
43 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 65 DCHECK(!callback_); |
44 callback_.func, callback_.user_data, retval_))); | 66 } |
| 67 |
| 68 int32_t EnterBase::SetResult(int32_t result) { |
| 69 if (!callback_) { |
| 70 // It doesn't make sense to call SetResult if there is no callback. |
| 71 NOTREACHED(); |
| 72 retval_ = result; |
| 73 return result; |
| 74 } |
| 75 if (result == PP_OK_COMPLETIONPENDING) { |
| 76 retval_ = result; |
| 77 if (callback_->is_blocking()) { |
| 78 DCHECK(!IsMainThread()); // We should have returned an error before this. |
| 79 retval_ = callback_->BlockUntilComplete(); |
| 80 } else { |
| 81 // The callback is not blocking and the operation will complete |
| 82 // asynchronously, so there's nothing to do. |
| 83 retval_ = result; |
| 84 } |
| 85 } else { |
| 86 // The function completed synchronously. |
| 87 if (callback_->is_required()) { |
| 88 // This is a required callback, so we must issue it asynchronously. |
| 89 // TODO(dmichael) make this work so that a call from a background thread |
| 90 // goes back to that thread. |
| 91 callback_->PostRun(result); |
| 92 retval_ = PP_OK_COMPLETIONPENDING; |
| 93 } else { |
| 94 // The callback is blocking or optional, so all we need to do is mark |
| 95 // the callback as completed so that it won't be issued later. |
| 96 callback_->MarkAsCompleted(); |
| 97 retval_ = result; |
| 98 } |
| 99 } |
| 100 callback_ = NULL; |
| 101 return retval_; |
| 102 } |
| 103 |
| 104 // static |
| 105 Resource* EnterBase::GetResource(PP_Resource resource) { |
| 106 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); |
| 107 } |
| 108 |
| 109 void EnterBase::SetStateForCallbackError(bool report_error) { |
| 110 if (!CallbackIsValid()) { |
| 111 callback_->MarkAsCompleted(); |
| 112 callback_ = NULL; |
| 113 retval_ = PP_ERROR_BLOCKS_MAIN_THREAD; |
| 114 if (report_error) { |
| 115 std::string message( |
| 116 "Blocking callbacks are not allowed on the main thread."); |
| 117 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, |
| 118 std::string(), message); |
| 119 } |
45 } | 120 } |
46 } | 121 } |
47 | 122 |
48 int32_t EnterBase::SetResult(int32_t result) { | 123 bool EnterBase::CallbackIsValid() const { |
49 if (!callback_.func || result == PP_OK_COMPLETIONPENDING) { | 124 // A callback is only considered invalid if it is blocking and we're on the |
50 // Easy case, we don't need to issue the callback (either none is | 125 // main thread. |
51 // required, or the implementation will asynchronously issue it | 126 return !callback_ || !callback_->is_blocking() || !IsMainThread(); |
52 // for us), just store the result. | |
53 callback_ = PP_BlockUntilComplete(); | |
54 retval_ = result; | |
55 return retval_; | |
56 } | |
57 | |
58 // This is a required callback, asynchronously issue it. | |
59 // TODO(brettw) make this work on different threads, etc. | |
60 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | |
61 callback_.func, callback_.user_data, result))); | |
62 | |
63 // Now that the callback will be issued in the future, we should return | |
64 // "pending" to the caller, and not issue the callback again. | |
65 callback_ = PP_BlockUntilComplete(); | |
66 retval_ = PP_OK_COMPLETIONPENDING; | |
67 return retval_; | |
68 } | 127 } |
69 | 128 |
70 Resource* EnterBase::GetResource(PP_Resource resource) const { | 129 void EnterBase::ClearCallback() { |
71 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); | 130 callback_ = NULL; |
72 } | 131 } |
73 | 132 |
74 void EnterBase::SetStateForResourceError(PP_Resource pp_resource, | 133 void EnterBase::SetStateForResourceError(PP_Resource pp_resource, |
75 Resource* resource_base, | 134 Resource* resource_base, |
76 void* object, | 135 void* object, |
77 bool report_error) { | 136 bool report_error) { |
| 137 // Check for callback errors. If we get any, SetStateForCallbackError will |
| 138 // emit a log message. But we also want to check for resource errors. If there |
| 139 // are both kinds of errors, we'll emit two log messages and return |
| 140 // PP_ERROR_BADRESOURCE. |
| 141 SetStateForCallbackError(report_error); |
| 142 |
78 if (object) | 143 if (object) |
79 return; // Everything worked. | 144 return; // Everything worked. |
80 | 145 |
81 if (callback_.func) { | 146 if (callback_ && callback_->is_required()) { |
82 // Required callback, issue the async completion. | 147 // TODO(dmichael) make this work so that a call from a background thread |
83 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 148 // goes back to that thread. |
84 callback_.func, callback_.user_data, | 149 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE)); |
85 static_cast<int32_t>(PP_ERROR_BADRESOURCE)))); | 150 callback_ = NULL; |
86 callback_ = PP_BlockUntilComplete(); | |
87 retval_ = PP_OK_COMPLETIONPENDING; | 151 retval_ = PP_OK_COMPLETIONPENDING; |
88 } else { | 152 } else { |
| 153 if (callback_) |
| 154 callback_->MarkAsCompleted(); |
| 155 callback_ = NULL; |
89 retval_ = PP_ERROR_BADRESOURCE; | 156 retval_ = PP_ERROR_BADRESOURCE; |
90 } | 157 } |
91 | 158 |
92 // We choose to silently ignore the error when the pp_resource is null | 159 // We choose to silently ignore the error when the pp_resource is null |
93 // because this is a pretty common case and we don't want to have lots | 160 // because this is a pretty common case and we don't want to have lots |
94 // of errors in the log. This should be an obvious case to debug. | 161 // of errors in the log. This should be an obvious case to debug. |
95 if (report_error && pp_resource) { | 162 if (report_error && pp_resource) { |
96 std::string message; | 163 std::string message; |
97 if (resource_base) { | 164 if (resource_base) { |
98 message = base::StringPrintf( | 165 message = base::StringPrintf( |
99 "0x%X is not the correct type for this function.", | 166 "0x%X is not the correct type for this function.", |
100 pp_resource); | 167 pp_resource); |
101 } else { | 168 } else { |
102 message = base::StringPrintf( | 169 message = base::StringPrintf( |
103 "0x%X is not a valid resource ID.", | 170 "0x%X is not a valid resource ID.", |
104 pp_resource); | 171 pp_resource); |
105 } | 172 } |
106 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, | 173 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, |
107 std::string(), message); | 174 std::string(), message); |
108 } | 175 } |
109 } | 176 } |
110 | 177 |
111 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, | 178 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, |
112 void* object, | 179 void* object, |
113 bool report_error) { | 180 bool report_error) { |
| 181 // Check for callback errors. If we get any, SetStateForCallbackError will |
| 182 // emit a log message. But we also want to check for instance errors. If there |
| 183 // are both kinds of errors, we'll emit two log messages and return |
| 184 // PP_ERROR_BADARGUMENT. |
| 185 SetStateForCallbackError(report_error); |
| 186 |
114 if (object) | 187 if (object) |
115 return; // Everything worked. | 188 return; // Everything worked. |
116 | 189 |
117 if (callback_.func) { | 190 if (callback_ && callback_->is_required()) { |
118 // Required callback, issue the async completion. | 191 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT)); |
119 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 192 callback_ = NULL; |
120 callback_.func, callback_.user_data, | |
121 static_cast<int32_t>(PP_ERROR_BADARGUMENT)))); | |
122 callback_ = PP_BlockUntilComplete(); | |
123 retval_ = PP_OK_COMPLETIONPENDING; | 193 retval_ = PP_OK_COMPLETIONPENDING; |
124 } else { | 194 } else { |
| 195 if (callback_) |
| 196 callback_->MarkAsCompleted(); |
| 197 callback_ = NULL; |
125 retval_ = PP_ERROR_BADARGUMENT; | 198 retval_ = PP_ERROR_BADARGUMENT; |
126 } | 199 } |
127 | 200 |
128 // We choose to silently ignore the error when the pp_instance is null as | 201 // We choose to silently ignore the error when the pp_instance is null as |
129 // for PP_Resources above. | 202 // for PP_Resources above. |
130 if (report_error && pp_instance) { | 203 if (report_error && pp_instance) { |
131 std::string message; | 204 std::string message; |
132 message = base::StringPrintf( | 205 message = base::StringPrintf( |
133 "0x%X is not a valid instance ID.", | 206 "0x%X is not a valid instance ID.", |
134 pp_instance); | 207 pp_instance); |
135 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, | 208 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, |
136 std::string(), message); | 209 std::string(), message); |
137 } | 210 } |
138 } | 211 } |
139 | 212 |
140 } // namespace subtle | 213 } // namespace subtle |
141 | 214 |
142 EnterInstance::EnterInstance(PP_Instance instance) | 215 EnterInstance::EnterInstance(PP_Instance instance) |
143 : EnterBase(), | 216 : EnterBase(), |
144 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { | 217 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { |
145 SetStateForFunctionError(instance, functions_, true); | 218 SetStateForFunctionError(instance, functions_, true); |
146 } | 219 } |
147 | 220 |
148 EnterInstance::EnterInstance(PP_Instance instance, | 221 EnterInstance::EnterInstance(PP_Instance instance, |
149 const PP_CompletionCallback& callback) | 222 const PP_CompletionCallback& callback) |
150 : EnterBase(callback), | 223 : EnterBase(0 /* resource */, callback), |
| 224 // TODO(dmichael): This means that the callback_ we get is not associated |
| 225 // even with the instance, but we should handle that for |
| 226 // MouseLock (maybe others?). |
151 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { | 227 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { |
152 SetStateForFunctionError(instance, functions_, true); | 228 SetStateForFunctionError(instance, functions_, true); |
153 } | 229 } |
154 | 230 |
155 EnterInstance::~EnterInstance() { | 231 EnterInstance::~EnterInstance() { |
156 } | 232 } |
157 | 233 |
158 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance) | 234 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance) |
159 : EnterBase(), | 235 : EnterBase(), |
160 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { | 236 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { |
(...skipping 16 matching lines...) Expand all Loading... |
177 : EnterBase(), | 253 : EnterBase(), |
178 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) { | 254 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) { |
179 SetStateForFunctionError(instance, functions_, true); | 255 SetStateForFunctionError(instance, functions_, true); |
180 } | 256 } |
181 | 257 |
182 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() { | 258 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() { |
183 } | 259 } |
184 | 260 |
185 } // namespace thunk | 261 } // namespace thunk |
186 } // namespace ppapi | 262 } // namespace ppapi |
OLD | NEW |