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

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: Cleaned up for review. Created 8 years, 8 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_)));
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
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
OLDNEW
« ppapi/thunk/enter.h ('K') | « 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