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

Side by Side Diff: ppapi/shared_impl/tracked_callback.cc

Issue 10081020: PPAPI: Make blocking completion callbacks work. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Updated TestURLLoader to test blocking callbacks. 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/shared_impl/tracked_callback.h" 5 #include "ppapi/shared_impl/tracked_callback.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/compiler_specific.h" 8 #include "base/compiler_specific.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/message_loop.h" 10 #include "base/message_loop.h"
11 #include "base/synchronization/lock.h"
12 #include "ppapi/c/dev/ppb_message_loop_dev.h"
11 #include "ppapi/c/pp_completion_callback.h" 13 #include "ppapi/c/pp_completion_callback.h"
12 #include "ppapi/c/pp_errors.h" 14 #include "ppapi/c/pp_errors.h"
13 #include "ppapi/shared_impl/callback_tracker.h" 15 #include "ppapi/shared_impl/callback_tracker.h"
14 #include "ppapi/shared_impl/ppapi_globals.h" 16 #include "ppapi/shared_impl/ppapi_globals.h"
15 #include "ppapi/shared_impl/proxy_lock.h" 17 #include "ppapi/shared_impl/proxy_lock.h"
16 #include "ppapi/shared_impl/resource.h" 18 #include "ppapi/shared_impl/resource.h"
17 19
18 namespace ppapi { 20 namespace ppapi {
19 21
20 // TrackedCallback ------------------------------------------------------------- 22 // TrackedCallback -------------------------------------------------------------
21 23
22 // Note: don't keep a Resource* since it may go out of scope before us. 24 // Note: don't keep a Resource* since it may go out of scope before us.
23 TrackedCallback::TrackedCallback( 25 TrackedCallback::TrackedCallback(
24 Resource* resource, 26 Resource* resource,
25 const PP_CompletionCallback& callback) 27 const PP_CompletionCallback& callback)
26 : ALLOW_THIS_IN_INITIALIZER_LIST(abort_impl_factory_(this)), 28 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
27 resource_id_(resource->pp_resource()), 29 resource_id_(resource ? resource->pp_resource() : 0),
28 completed_(false), 30 completed_(false),
29 aborted_(false), 31 aborted_(false),
30 callback_(callback) { 32 callback_(callback),
31 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( 33 result_for_blocked_callback_(PP_OK) {
32 resource->pp_instance()), 34 // We can only track this callback if the resource is valid. It can be NULL
33 tracker_->Add(make_scoped_refptr(this)); 35 // in error conditions in the Enter* classes, and callbacks which aren't in
36 // the tracker will generally be short-lived.
37 // TODO(dmichael): Add tracking at the instance level, for callbacks that only
38 // have an instance.
39 if (resource) {
40 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance(
41 resource->pp_instance());
42 tracker_->Add(make_scoped_refptr(this));
43 }
44
45 if (base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock())
46 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock));
34 } 47 }
35 48
36 TrackedCallback::~TrackedCallback() { 49 TrackedCallback::~TrackedCallback() {
37 } 50 }
38 51
39 void TrackedCallback::Abort() { 52 void TrackedCallback::Abort() {
40 if (!completed()) { 53 if (!completed()) {
41 aborted_ = true; 54 aborted_ = true;
42 Run(PP_ERROR_ABORTED); 55 Run(PP_ERROR_ABORTED);
43 } 56 }
44 } 57 }
45 58
46 void TrackedCallback::PostAbort() { 59 void TrackedCallback::PostAbort() {
47 if (!completed()) { 60 // It doesn't make sense to abort a callback that's not associated with a
61 // resource.
62 // TODO(dmichael): If we allow associating with an instance instead, we must
63 // allow for aborts in the case of the instance being destroyed.
64 DCHECK(resource_id_);
65
66 if (!completed() && !aborted_) {
48 aborted_ = true; 67 aborted_ = true;
49 // Post a task for the abort (only if necessary). 68 MessageLoop::current()->PostTask(
50 if (!abort_impl_factory_.HasWeakPtrs()) { 69 FROM_HERE,
51 MessageLoop::current()->PostTask( 70 RunWhileLocked(base::Bind(&TrackedCallback::Abort,
52 FROM_HERE, 71 weak_ptr_factory_.GetWeakPtr())));
53 RunWhileLocked(base::Bind(&TrackedCallback::Abort,
54 abort_impl_factory_.GetWeakPtr())));
55 }
56 } 72 }
57 } 73 }
58 74
59 void TrackedCallback::Run(int32_t result) { 75 void TrackedCallback::Run(int32_t result) {
60 if (!completed()) { 76 if (!completed()) {
61 // Cancel any pending calls. 77 // Cancel any pending calls.
62 abort_impl_factory_.InvalidateWeakPtrs(); 78 weak_ptr_factory_.InvalidateWeakPtrs();
63 79
64 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| 80 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()|
65 // may delete us. 81 // may delete us.
66 PP_CompletionCallback callback = callback_; 82 PP_CompletionCallback callback = callback_;
67 if (aborted()) 83 if (aborted())
68 result = PP_ERROR_ABORTED; 84 result = PP_ERROR_ABORTED;
69 85
70 // Do this before running the callback in case of reentrancy (which 86 if (is_blocking()) {
71 // shouldn't happen, but avoid strange failures). 87 // If the condition variable is invalid, we're probably running
72 MarkAsCompleted(); 88 // in-process, and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD
73 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); 89 // well before this.
90 DCHECK(operation_completed_condvar_.get());
91 result_for_blocked_callback_ = result;
92 // Retain ourselves, since MarkAsCompleted will remove us from the
93 // tracker. Then MarkAsCompleted before waking up the blocked thread,
94 // which could potentially re-enter.
95 scoped_refptr<TrackedCallback> thiz(this);
96 MarkAsCompleted();
97 // Wake up the blocked thread. See BlockUntilComplete for where the thread
98 // Wait()s.
99 operation_completed_condvar_->Signal();
100 } else {
101 // Do this before running the callback in case of reentrancy (which
102 // shouldn't happen, but avoid strange failures).
103 MarkAsCompleted();
104 // TODO(dmichael): Associate a message loop with the callback; if it's not
105 // the same as the current thread's loop, then post it to the right loop.
106 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result);
107 }
74 } 108 }
75 } 109 }
76 110
111 void TrackedCallback::PostRun(int32_t result) {
112 DCHECK(!completed());
113 if (!completed()) {
114 // There should be no pending calls.
115 DCHECK(!weak_ptr_factory_.HasWeakPtrs());
116 weak_ptr_factory_.InvalidateWeakPtrs();
117
118 if (resource_id_) {
119 // If it has a resource_id_, it's in the tracker, and may be aborted if
120 // the resource is destroyed.
121 MessageLoop::current()->PostTask(
122 FROM_HERE,
123 RunWhileLocked(base::Bind(&TrackedCallback::Run,
124 weak_ptr_factory_.GetWeakPtr(),
125 result)));
126 } else {
127 // There is no resource_id_ associated with this callback, so it can't be
128 // aborted. We have the message loop retain the callback to make sure it
129 // gets run. This can happen when EnterBase is given an invalid resource,
130 // and in that case no resource or instance will retain this
131 // TrackedCallback.
132 MessageLoop::current()->PostTask(
133 FROM_HERE,
134 RunWhileLocked(base::Bind(&TrackedCallback::Run,
135 this,
136 result)));
137 }
138 }
139 }
140
77 // static 141 // static
78 bool TrackedCallback::IsPending( 142 bool TrackedCallback::IsPending(
79 const scoped_refptr<TrackedCallback>& callback) { 143 const scoped_refptr<TrackedCallback>& callback) {
80 if (!callback.get()) 144 if (!callback.get())
81 return false; 145 return false;
82 return !callback->completed(); 146 return !callback->completed();
83 } 147 }
84 148
85 // static 149 // static
86 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback, 150 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback,
87 int32_t result) { 151 int32_t result) {
88 scoped_refptr<TrackedCallback> temp; 152 scoped_refptr<TrackedCallback> temp;
89 temp.swap(*callback); 153 temp.swap(*callback);
90 temp->Run(result); 154 temp->Run(result);
91 } 155 }
92 156
93 // static 157 // static
94 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) { 158 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) {
95 scoped_refptr<TrackedCallback> temp; 159 scoped_refptr<TrackedCallback> temp;
96 temp.swap(*callback); 160 temp.swap(*callback);
97 temp->Abort(); 161 temp->Abort();
98 } 162 }
99 163
164 int32_t TrackedCallback::BlockUntilComplete() {
165 // Note, we are already holding the proxy lock in all these methods, including
166 // this one (see ppapi/thunk/enter.cc for where it gets acquired).
167
168 // It doesn't make sense to wait on a non-blocking callback.
169 DCHECK(is_blocking());
170 // BlockUntilComplete should never be called for in-process plugins, where
171 // blocking callbacks are not supported.
172 DCHECK(operation_completed_condvar_.get());
173
174 while (!completed())
175 operation_completed_condvar_->Wait();
176 return result_for_blocked_callback_;
177 }
178
100 void TrackedCallback::MarkAsCompleted() { 179 void TrackedCallback::MarkAsCompleted() {
101 DCHECK(!completed()); 180 DCHECK(!completed());
102 181
103 // We will be removed; maintain a reference to ensure we won't be deleted 182 // We will be removed; maintain a reference to ensure we won't be deleted
104 // until we're done. 183 // until we're done.
105 scoped_refptr<TrackedCallback> thiz = this; 184 scoped_refptr<TrackedCallback> thiz = this;
106 completed_ = true; 185 completed_ = true;
107 tracker_->Remove(thiz); 186 // We may not have a valid resource, in which case we're not in the tracker.
187 if (resource_id_)
188 tracker_->Remove(thiz);
108 tracker_ = NULL; 189 tracker_ = NULL;
109 } 190 }
110 191
111 } // namespace ppapi 192 } // namespace ppapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698