Chromium Code Reviews| 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 "ppapi/c/pp_errors.h" | 11 #include "ppapi/c/pp_errors.h" |
| 12 #include "ppapi/shared_impl/ppapi_globals.h" | 12 #include "ppapi/shared_impl/ppapi_globals.h" |
| 13 #include "ppapi/shared_impl/tracked_callback.h" | |
| 13 #include "ppapi/thunk/ppb_instance_api.h" | 14 #include "ppapi/thunk/ppb_instance_api.h" |
| 14 #include "ppapi/thunk/resource_creation_api.h" | 15 #include "ppapi/thunk/resource_creation_api.h" |
| 15 | 16 |
| 16 namespace ppapi { | 17 namespace ppapi { |
| 18 namespace { | |
| 19 | |
| 20 bool IsMainThread() { | |
| 21 return | |
| 22 PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread(); | |
| 23 } | |
| 24 | |
| 25 } // namespace | |
| 26 | |
| 17 namespace thunk { | 27 namespace thunk { |
| 18 | 28 |
| 19 namespace subtle { | 29 namespace subtle { |
| 20 | 30 |
| 21 bool CallbackIsRequired(const PP_CompletionCallback& callback) { | 31 EnterBase::EnterBase() |
| 22 return callback.func != NULL && | 32 : resource_(NULL), |
| 23 (callback.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL) == 0; | 33 retval_(PP_OK) { |
| 34 // TODO(dmichael) validate that threads have an associated message loop. | |
| 24 } | 35 } |
| 25 | 36 |
| 26 EnterBase::EnterBase() | 37 EnterBase::EnterBase(PP_Resource resource) |
| 27 : callback_(PP_BlockUntilComplete()), | 38 : resource_(GetResource(resource)), |
| 28 retval_(PP_OK) { | 39 retval_(PP_OK) { |
| 29 // TODO(brettw) validate threads. | 40 // TODO(dmichael) validate that threads have an associated message loop. |
| 30 } | 41 } |
| 31 | 42 |
| 32 EnterBase::EnterBase(const PP_CompletionCallback& callback) | 43 EnterBase::EnterBase(PP_Resource resource, |
| 33 : callback_(CallbackIsRequired(callback) ? callback | 44 const PP_CompletionCallback& callback) |
| 34 : PP_BlockUntilComplete()), | 45 : resource_(GetResource(resource)), |
| 35 retval_(PP_OK) { | 46 retval_(PP_OK) { |
| 36 // TODO(brettw) validate threads. | 47 callback_ = new TrackedCallback(resource_, callback); |
| 48 | |
| 49 // TODO(dmichael) validate that threads have an associated message loop. | |
| 37 } | 50 } |
| 38 | 51 |
| 39 EnterBase::~EnterBase() { | 52 EnterBase::~EnterBase() { |
| 40 if (callback_.func) { | 53 // 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(). | 54 // know it will be completed asynchronously. So by this point it should be |
| 42 DCHECK(retval_ != PP_OK_COMPLETIONPENDING && retval_ != PP_OK); | 55 // NULL. |
| 43 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 56 DCHECK(!callback_); |
| 44 callback_.func, callback_.user_data, retval_))); | 57 } |
| 58 | |
| 59 int32_t EnterBase::SetResult(int32_t result) { | |
| 60 if (!callback_) { | |
| 61 // It doesn't make sense to call SetResult if there is no callback. | |
|
brettw
2012/05/20 17:46:46
I don't see why you "shouldn't" call this when the
dmichael (off chromium)
2012/05/22 18:08:39
I didn't really want to have to code for that case
| |
| 62 NOTREACHED(); | |
| 63 retval_ = result; | |
| 64 return result; | |
| 65 } | |
| 66 if (result == PP_OK_COMPLETIONPENDING) { | |
| 67 retval_ = result; | |
| 68 if (callback_->is_blocking()) { | |
| 69 DCHECK(!IsMainThread()); // We should have returned an error before this. | |
| 70 retval_ = callback_->BlockUntilComplete(); | |
| 71 } else { | |
| 72 // The callback is not blocking and the operation will complete | |
| 73 // asynchronously, so there's nothing to do. | |
| 74 retval_ = result; | |
| 75 } | |
| 76 } else { | |
| 77 // The function completed synchronously. | |
| 78 if (callback_->is_required()) { | |
| 79 // This is a required callback, so we must issue it asynchronously. | |
| 80 // TODO(dmichael) make this work so that a call from a background thread | |
| 81 // goes back to that thread. | |
| 82 callback_->PostRun(result); | |
| 83 retval_ = PP_OK_COMPLETIONPENDING; | |
| 84 } else { | |
| 85 // The callback is blocking or optional, so all we need to do is mark | |
| 86 // the callback as completed so that it won't be issued later. | |
| 87 callback_->MarkAsCompleted(); | |
| 88 retval_ = result; | |
| 89 } | |
| 90 } | |
| 91 callback_ = NULL; | |
| 92 return retval_; | |
| 93 } | |
| 94 | |
| 95 // static | |
| 96 Resource* EnterBase::GetResource(PP_Resource resource) { | |
| 97 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); | |
| 98 } | |
| 99 | |
| 100 void EnterBase::SetStateForCallbackError(bool report_error) { | |
| 101 if (!CallbackIsValid()) { | |
| 102 callback_->MarkAsCompleted(); | |
| 103 callback_ = NULL; | |
| 104 retval_ = PP_ERROR_BLOCKS_MAIN_THREAD; | |
| 105 if (report_error) { | |
| 106 std::string message( | |
| 107 "Blocking callbacks are not allowed on the main thread."); | |
| 108 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, | |
| 109 std::string(), message); | |
| 110 } | |
| 45 } | 111 } |
| 46 } | 112 } |
| 47 | 113 |
| 48 int32_t EnterBase::SetResult(int32_t result) { | 114 bool EnterBase::CallbackIsValid() const { |
| 49 if (!callback_.func || result == PP_OK_COMPLETIONPENDING) { | 115 // 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 | 116 // main thread. |
| 51 // required, or the implementation will asynchronously issue it | 117 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 } | 118 } |
| 69 | 119 |
| 70 Resource* EnterBase::GetResource(PP_Resource resource) const { | 120 void EnterBase::ClearCallback() { |
| 71 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); | 121 callback_ = NULL; |
| 72 } | 122 } |
| 73 | 123 |
| 74 void EnterBase::SetStateForResourceError(PP_Resource pp_resource, | 124 void EnterBase::SetStateForResourceError(PP_Resource pp_resource, |
| 75 Resource* resource_base, | 125 Resource* resource_base, |
| 76 void* object, | 126 void* object, |
| 77 bool report_error) { | 127 bool report_error) { |
| 128 SetStateForCallbackError(report_error); | |
|
brettw
2012/05/20 17:46:46
This function call should return early on error. I
dmichael (off chromium)
2012/05/22 18:08:39
My intent was to have both log messages come out,
brettw
2012/06/01 21:11:32
Did you update the documentation? I don't see anyt
| |
| 129 | |
| 78 if (object) | 130 if (object) |
| 79 return; // Everything worked. | 131 return; // Everything worked. |
| 80 | 132 |
| 81 if (callback_.func) { | 133 if (callback_ && callback_->is_required()) { |
| 82 // Required callback, issue the async completion. | 134 // TODO(dmichael) make this work so that a call from a background thread |
| 83 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 135 // goes back to that thread. |
| 84 callback_.func, callback_.user_data, | 136 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE)); |
| 85 static_cast<int32_t>(PP_ERROR_BADRESOURCE)))); | 137 callback_ = NULL; |
| 86 callback_ = PP_BlockUntilComplete(); | |
| 87 retval_ = PP_OK_COMPLETIONPENDING; | 138 retval_ = PP_OK_COMPLETIONPENDING; |
| 88 } else { | 139 } else { |
| 140 if (callback_) | |
| 141 callback_->MarkAsCompleted(); | |
| 142 callback_ = NULL; | |
| 89 retval_ = PP_ERROR_BADRESOURCE; | 143 retval_ = PP_ERROR_BADRESOURCE; |
| 90 } | 144 } |
| 91 | 145 |
| 92 // We choose to silently ignore the error when the pp_resource is null | 146 // 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 | 147 // 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. | 148 // of errors in the log. This should be an obvious case to debug. |
| 95 if (report_error && pp_resource) { | 149 if (report_error && pp_resource) { |
| 96 std::string message; | 150 std::string message; |
| 97 if (resource_base) { | 151 if (resource_base) { |
| 98 message = base::StringPrintf( | 152 message = base::StringPrintf( |
| 99 "0x%X is not the correct type for this function.", | 153 "0x%X is not the correct type for this function.", |
| 100 pp_resource); | 154 pp_resource); |
| 101 } else { | 155 } else { |
| 102 message = base::StringPrintf( | 156 message = base::StringPrintf( |
| 103 "0x%X is not a valid resource ID.", | 157 "0x%X is not a valid resource ID.", |
| 104 pp_resource); | 158 pp_resource); |
| 105 } | 159 } |
| 106 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, | 160 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, |
| 107 std::string(), message); | 161 std::string(), message); |
| 108 } | 162 } |
| 109 } | 163 } |
| 110 | 164 |
| 111 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, | 165 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, |
| 112 void* object, | 166 void* object, |
| 113 bool report_error) { | 167 bool report_error) { |
| 168 SetStateForCallbackError(report_error); | |
| 169 | |
| 114 if (object) | 170 if (object) |
| 115 return; // Everything worked. | 171 return; // Everything worked. |
| 116 | 172 |
| 117 if (callback_.func) { | 173 if (callback_ && callback_->is_required()) { |
| 118 // Required callback, issue the async completion. | 174 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT)); |
| 119 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 175 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; | 176 retval_ = PP_OK_COMPLETIONPENDING; |
| 124 } else { | 177 } else { |
| 178 if (callback_) | |
| 179 callback_->MarkAsCompleted(); | |
| 180 callback_ = NULL; | |
| 125 retval_ = PP_ERROR_BADARGUMENT; | 181 retval_ = PP_ERROR_BADARGUMENT; |
| 126 } | 182 } |
| 127 | 183 |
| 128 // We choose to silently ignore the error when the pp_instance is null as | 184 // We choose to silently ignore the error when the pp_instance is null as |
| 129 // for PP_Resources above. | 185 // for PP_Resources above. |
| 130 if (report_error && pp_instance) { | 186 if (report_error && pp_instance) { |
| 131 std::string message; | 187 std::string message; |
| 132 message = base::StringPrintf( | 188 message = base::StringPrintf( |
| 133 "0x%X is not a valid instance ID.", | 189 "0x%X is not a valid instance ID.", |
| 134 pp_instance); | 190 pp_instance); |
| 135 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, | 191 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, |
| 136 std::string(), message); | 192 std::string(), message); |
| 137 } | 193 } |
| 138 } | 194 } |
| 139 | 195 |
| 140 } // namespace subtle | 196 } // namespace subtle |
| 141 | 197 |
| 142 EnterInstance::EnterInstance(PP_Instance instance) | 198 EnterInstance::EnterInstance(PP_Instance instance) |
| 143 : EnterBase(), | 199 : EnterBase(), |
| 144 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { | 200 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { |
| 145 SetStateForFunctionError(instance, functions_, true); | 201 SetStateForFunctionError(instance, functions_, true); |
| 146 } | 202 } |
| 147 | 203 |
| 148 EnterInstance::EnterInstance(PP_Instance instance, | 204 EnterInstance::EnterInstance(PP_Instance instance, |
| 149 const PP_CompletionCallback& callback) | 205 const PP_CompletionCallback& callback) |
| 150 : EnterBase(callback), | 206 : EnterBase(0 /* resource */, callback), |
| 207 // TODO(dmichael): This means that the callback_ we get is not associated | |
| 208 // even with the instance, but we should handle that for | |
| 209 // MouseLock (maybe others?). | |
| 151 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { | 210 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { |
| 152 SetStateForFunctionError(instance, functions_, true); | 211 SetStateForFunctionError(instance, functions_, true); |
| 153 } | 212 } |
| 154 | 213 |
| 155 EnterInstance::~EnterInstance() { | 214 EnterInstance::~EnterInstance() { |
| 156 } | 215 } |
| 157 | 216 |
| 158 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance) | 217 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance) |
| 159 : EnterBase(), | 218 : EnterBase(), |
| 160 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { | 219 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 177 : EnterBase(), | 236 : EnterBase(), |
| 178 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) { | 237 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) { |
| 179 SetStateForFunctionError(instance, functions_, true); | 238 SetStateForFunctionError(instance, functions_, true); |
| 180 } | 239 } |
| 181 | 240 |
| 182 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() { | 241 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() { |
| 183 } | 242 } |
| 184 | 243 |
| 185 } // namespace thunk | 244 } // namespace thunk |
| 186 } // namespace ppapi | 245 } // namespace ppapi |
| OLD | NEW |