Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(216)

Side by Side Diff: ppapi/thunk/enter.cc

Issue 10081020: PPAPI: Make blocking completion callbacks work. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: try again Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698