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_))); | |
45 } | |
46 } | 57 } |
47 | 58 |
48 int32_t EnterBase::SetResult(int32_t result) { | 59 int32_t EnterBase::SetResult(int32_t result) { |
49 if (!callback_.func || result == PP_OK_COMPLETIONPENDING) { | 60 if (!callback_) { |
50 // Easy case, we don't need to issue the callback (either none is | 61 // It doesn't make sense to call SetResult if there is no callback. |
51 // required, or the implementation will asynchronously issue it | 62 NOTREACHED(); |
52 // for us), just store the result. | |
53 callback_ = PP_BlockUntilComplete(); | |
54 retval_ = result; | 63 retval_ = result; |
55 return retval_; | 64 return result; |
56 } | 65 } |
57 | 66 if (result == PP_OK_COMPLETIONPENDING) { |
58 // This is a required callback, asynchronously issue it. | 67 retval_ = result; |
59 // TODO(brettw) make this work on different threads, etc. | 68 if (callback_->is_blocking()) { |
60 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 69 DCHECK(!IsMainThread()); // We should have returned an error before this. |
61 callback_.func, callback_.user_data, result))); | 70 retval_ = callback_->BlockUntilComplete(); |
62 | 71 } else { |
63 // Now that the callback will be issued in the future, we should return | 72 // The callback is not blocking and the operation will complete |
64 // "pending" to the caller, and not issue the callback again. | 73 // asynchronously, so there's nothing to do. |
65 callback_ = PP_BlockUntilComplete(); | 74 retval_ = result; |
66 retval_ = PP_OK_COMPLETIONPENDING; | 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; |
67 return retval_; | 92 return retval_; |
68 } | 93 } |
69 | 94 |
70 FunctionGroupBase* EnterBase::GetFunctions(PP_Instance instance, | 95 FunctionGroupBase* EnterBase::GetFunctions(PP_Instance instance, |
71 ApiID id) const { | 96 ApiID id) const { |
72 return PpapiGlobals::Get()->GetFunctionAPI(instance, id); | 97 return PpapiGlobals::Get()->GetFunctionAPI(instance, id); |
73 } | 98 } |
74 | 99 |
75 Resource* EnterBase::GetResource(PP_Resource resource) const { | 100 // static |
| 101 Resource* EnterBase::GetResource(PP_Resource resource) { |
76 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); | 102 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); |
77 } | 103 } |
78 | 104 |
| 105 void EnterBase::SetStateForCallbackError(bool report_error) { |
| 106 if (!CallbackIsValid()) { |
| 107 callback_->MarkAsCompleted(); |
| 108 callback_ = NULL; |
| 109 retval_ = PP_ERROR_BLOCKS_MAIN_THREAD; |
| 110 if (report_error) { |
| 111 std::string message( |
| 112 "Blocking callbacks are not allowed on the main thread."); |
| 113 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, |
| 114 std::string(), message); |
| 115 } |
| 116 } |
| 117 } |
| 118 |
| 119 bool EnterBase::CallbackIsValid() const { |
| 120 // A callback is only considered invalid if it is blocking and we're on the |
| 121 // main thread. |
| 122 return !callback_ || !callback_->is_blocking() || !IsMainThread(); |
| 123 } |
| 124 |
| 125 void EnterBase::ClearCallback() { |
| 126 callback_ = NULL; |
| 127 } |
| 128 |
79 void EnterBase::SetStateForResourceError(PP_Resource pp_resource, | 129 void EnterBase::SetStateForResourceError(PP_Resource pp_resource, |
80 Resource* resource_base, | 130 Resource* resource_base, |
81 void* object, | 131 void* object, |
82 bool report_error) { | 132 bool report_error) { |
83 if (object) | 133 if (object) |
84 return; // Everything worked. | 134 return; // Everything worked. |
85 | 135 |
86 if (callback_.func) { | 136 if (callback_ && callback_->is_required()) { |
87 // Required callback, issue the async completion. | 137 // TODO(dmichael) make this work so that a call from a background thread |
88 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 138 // goes back to that thread. |
89 callback_.func, callback_.user_data, | 139 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE)); |
90 static_cast<int32_t>(PP_ERROR_BADRESOURCE)))); | 140 callback_ = NULL; |
91 callback_ = PP_BlockUntilComplete(); | |
92 retval_ = PP_OK_COMPLETIONPENDING; | 141 retval_ = PP_OK_COMPLETIONPENDING; |
93 } else { | 142 } else { |
| 143 if (callback_) |
| 144 callback_->MarkAsCompleted(); |
| 145 callback_ = NULL; |
94 retval_ = PP_ERROR_BADRESOURCE; | 146 retval_ = PP_ERROR_BADRESOURCE; |
95 } | 147 } |
96 | 148 |
97 // We choose to silently ignore the error when the pp_resource is null | 149 // We choose to silently ignore the error when the pp_resource is null |
98 // because this is a pretty common case and we don't want to have lots | 150 // because this is a pretty common case and we don't want to have lots |
99 // of errors in the log. This should be an obvious case to debug. | 151 // of errors in the log. This should be an obvious case to debug. |
100 if (report_error && pp_resource) { | 152 if (report_error && pp_resource) { |
101 std::string message; | 153 std::string message; |
102 if (resource_base) { | 154 if (resource_base) { |
103 message = base::StringPrintf( | 155 message = base::StringPrintf( |
104 "0x%X is not the correct type for this function.", | 156 "0x%X is not the correct type for this function.", |
105 pp_resource); | 157 pp_resource); |
106 } else { | 158 } else { |
107 message = base::StringPrintf( | 159 message = base::StringPrintf( |
108 "0x%X is not a valid resource ID.", | 160 "0x%X is not a valid resource ID.", |
109 pp_resource); | 161 pp_resource); |
110 } | 162 } |
111 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, | 163 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, |
112 std::string(), message); | 164 std::string(), message); |
113 } | 165 } |
114 } | 166 } |
115 | 167 |
116 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, | 168 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, |
117 void* object, | 169 void* object, |
118 bool report_error) { | 170 bool report_error) { |
119 if (object) | 171 if (object) |
120 return; // Everything worked. | 172 return; // Everything worked. |
121 | 173 |
122 if (callback_.func) { | 174 if (callback_ && callback_->is_required()) { |
123 // Required callback, issue the async completion. | 175 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT)); |
124 MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind( | 176 callback_ = NULL; |
125 callback_.func, callback_.user_data, | |
126 static_cast<int32_t>(PP_ERROR_BADARGUMENT)))); | |
127 callback_ = PP_BlockUntilComplete(); | |
128 retval_ = PP_OK_COMPLETIONPENDING; | 177 retval_ = PP_OK_COMPLETIONPENDING; |
129 } else { | 178 } else { |
| 179 if (callback_) |
| 180 callback_->MarkAsCompleted(); |
| 181 callback_ = NULL; |
130 retval_ = PP_ERROR_BADARGUMENT; | 182 retval_ = PP_ERROR_BADARGUMENT; |
131 } | 183 } |
132 | 184 |
133 // We choose to silently ignore the error when the pp_instance is null as | 185 // We choose to silently ignore the error when the pp_instance is null as |
134 // for PP_Resources above. | 186 // for PP_Resources above. |
135 if (report_error && pp_instance) { | 187 if (report_error && pp_instance) { |
136 std::string message; | 188 std::string message; |
137 message = base::StringPrintf( | 189 message = base::StringPrintf( |
138 "0x%X is not a valid instance ID.", | 190 "0x%X is not a valid instance ID.", |
139 pp_instance); | 191 pp_instance); |
(...skipping 16 matching lines...) Expand all Loading... |
156 } | 208 } |
157 | 209 |
158 EnterInstance::EnterInstance(PP_Instance instance, | 210 EnterInstance::EnterInstance(PP_Instance instance, |
159 const PP_CompletionCallback& callback) | 211 const PP_CompletionCallback& callback) |
160 : EnterFunction<PPB_Instance_FunctionAPI>(instance, callback, true) { | 212 : EnterFunction<PPB_Instance_FunctionAPI>(instance, callback, true) { |
161 } | 213 } |
162 | 214 |
163 EnterInstance::~EnterInstance() { | 215 EnterInstance::~EnterInstance() { |
164 } | 216 } |
165 | 217 |
| 218 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance) |
| 219 : EnterFunctionNoLock<PPB_Instance_FunctionAPI>(instance, true) { |
| 220 } |
| 221 |
| 222 EnterInstanceNoLock::~EnterInstanceNoLock() { |
| 223 } |
| 224 |
166 } // namespace thunk | 225 } // namespace thunk |
167 } // namespace ppapi | 226 } // namespace ppapi |
OLD | NEW |