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

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: export AssertLockHeld Created 8 years, 6 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
« no previous file with comments | « ppapi/thunk/enter.h ('k') | ppapi/thunk/ppb_audio_api.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "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
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
OLDNEW
« no previous file with comments | « ppapi/thunk/enter.h ('k') | ppapi/thunk/ppb_audio_api.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698