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

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: 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/shared_impl/tracked_callback.h ('k') | ppapi/tests/test_broker.cc » ('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/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 for callbacks associated with
36 // an instance.
37 // TODO(dmichael): Add tracking at the instance level, for callbacks that only
38 // have an instance (e.g. for MouseLock).
39 if (resource) {
40 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance(
41 resource->pp_instance());
42 tracker_->Add(make_scoped_refptr(this));
43 }
44
45 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock();
46 // We only need a ConditionVariable if the lock is valid (i.e., we're out-of-
47 // process) and the callback is blocking.
48 if (proxy_lock && is_blocking())
49 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock));
34 } 50 }
35 51
36 TrackedCallback::~TrackedCallback() { 52 TrackedCallback::~TrackedCallback() {
37 } 53 }
38 54
39 void TrackedCallback::Abort() { 55 void TrackedCallback::Abort() {
40 if (!completed()) { 56 if (!completed()) {
41 aborted_ = true; 57 aborted_ = true;
42 Run(PP_ERROR_ABORTED); 58 Run(PP_ERROR_ABORTED);
43 } 59 }
44 } 60 }
45 61
46 void TrackedCallback::PostAbort() { 62 void TrackedCallback::PostAbort() {
47 if (!completed()) { 63 // It doesn't make sense to abort a callback that's not associated with a
64 // resource.
65 // TODO(dmichael): If we allow associating with an instance instead, we must
66 // allow for aborts in the case of the instance being destroyed.
67 DCHECK(resource_id_);
68
69 if (!completed() && !aborted_) {
48 aborted_ = true; 70 aborted_ = true;
49 // Post a task for the abort (only if necessary). 71 MessageLoop::current()->PostTask(
50 if (!abort_impl_factory_.HasWeakPtrs()) { 72 FROM_HERE,
51 MessageLoop::current()->PostTask( 73 RunWhileLocked(base::Bind(&TrackedCallback::Abort,
52 FROM_HERE, 74 weak_ptr_factory_.GetWeakPtr())));
53 RunWhileLocked(base::Bind(&TrackedCallback::Abort,
54 abort_impl_factory_.GetWeakPtr())));
55 }
56 } 75 }
57 } 76 }
58 77
59 void TrackedCallback::Run(int32_t result) { 78 void TrackedCallback::Run(int32_t result) {
60 if (!completed()) { 79 if (!completed()) {
61 // Cancel any pending calls. 80 // Cancel any pending calls.
62 abort_impl_factory_.InvalidateWeakPtrs(); 81 weak_ptr_factory_.InvalidateWeakPtrs();
63 82
64 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| 83 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()|
65 // may delete us. 84 // may delete us.
66 PP_CompletionCallback callback = callback_; 85 PP_CompletionCallback callback = callback_;
67 if (aborted()) 86 if (aborted())
68 result = PP_ERROR_ABORTED; 87 result = PP_ERROR_ABORTED;
69 88
70 // Do this before running the callback in case of reentrancy (which 89 if (is_blocking()) {
71 // shouldn't happen, but avoid strange failures). 90 // If the condition variable is invalid, there are two possibilities. One,
72 MarkAsCompleted(); 91 // we're running in-process, in which case the call should have come in on
73 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result); 92 // the main thread and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD
93 // well before this. Otherwise, this callback was not created as a
94 // blocking callback. Either way, there's some internal error.
95 if (!operation_completed_condvar_.get()) {
96 NOTREACHED();
97 return;
98 }
99 result_for_blocked_callback_ = result;
100 // Retain ourselves, since MarkAsCompleted will remove us from the
101 // tracker. Then MarkAsCompleted before waking up the blocked thread,
102 // which could potentially re-enter.
103 scoped_refptr<TrackedCallback> thiz(this);
104 MarkAsCompleted();
105 // Wake up the blocked thread. See BlockUntilComplete for where the thread
106 // Wait()s.
107 operation_completed_condvar_->Signal();
108 } else {
109 // Do this before running the callback in case of reentrancy (which
110 // shouldn't happen, but avoid strange failures).
111 MarkAsCompleted();
112 // TODO(dmichael): Associate a message loop with the callback; if it's not
113 // the same as the current thread's loop, then post it to the right loop.
114 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result);
115 }
74 } 116 }
75 } 117 }
76 118
119 void TrackedCallback::PostRun(int32_t result) {
120 DCHECK(!completed());
121 if (!completed()) {
122 // There should be no pending calls.
123 DCHECK(!weak_ptr_factory_.HasWeakPtrs());
124 weak_ptr_factory_.InvalidateWeakPtrs();
125
126 if (resource_id_) {
127 // If it has a resource_id_, it's in the tracker, and may be aborted if
128 // the resource is destroyed.
129 MessageLoop::current()->PostTask(
130 FROM_HERE,
131 RunWhileLocked(base::Bind(&TrackedCallback::Run,
132 weak_ptr_factory_.GetWeakPtr(),
133 result)));
134 } else {
135 // There is no resource_id_ associated with this callback, so it can't be
136 // aborted. We have the message loop retain the callback to make sure it
137 // gets run. This can happen when EnterBase is given an invalid resource,
138 // and in that case no resource or instance will retain this
139 // TrackedCallback.
140 MessageLoop::current()->PostTask(
141 FROM_HERE,
142 RunWhileLocked(base::Bind(&TrackedCallback::Run,
143 this,
144 result)));
145 }
146 }
147 }
148
77 // static 149 // static
78 bool TrackedCallback::IsPending( 150 bool TrackedCallback::IsPending(
79 const scoped_refptr<TrackedCallback>& callback) { 151 const scoped_refptr<TrackedCallback>& callback) {
80 if (!callback.get()) 152 if (!callback.get())
81 return false; 153 return false;
82 return !callback->completed(); 154 return !callback->completed();
83 } 155 }
84 156
85 // static 157 // static
86 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback, 158 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback,
87 int32_t result) { 159 int32_t result) {
88 scoped_refptr<TrackedCallback> temp; 160 scoped_refptr<TrackedCallback> temp;
89 temp.swap(*callback); 161 temp.swap(*callback);
90 temp->Run(result); 162 temp->Run(result);
91 } 163 }
92 164
93 // static 165 // static
94 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) { 166 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) {
95 scoped_refptr<TrackedCallback> temp; 167 scoped_refptr<TrackedCallback> temp;
96 temp.swap(*callback); 168 temp.swap(*callback);
97 temp->Abort(); 169 temp->Abort();
98 } 170 }
99 171
172 int32_t TrackedCallback::BlockUntilComplete() {
173 // Note, we are already holding the proxy lock in all these methods, including
174 // this one (see ppapi/thunk/enter.cc for where it gets acquired).
175
176 // It doesn't make sense to wait on a non-blocking callback. Furthermore,
177 // BlockUntilComplete should never be called for in-process plugins, where
178 // blocking callbacks are not supported.
179 CHECK(operation_completed_condvar_.get());
180 if (!is_blocking() || !operation_completed_condvar_.get()) {
181 NOTREACHED();
182 return PP_ERROR_FAILED;
183 }
184
185 while (!completed())
186 operation_completed_condvar_->Wait();
187 return result_for_blocked_callback_;
188 }
189
100 void TrackedCallback::MarkAsCompleted() { 190 void TrackedCallback::MarkAsCompleted() {
101 DCHECK(!completed()); 191 DCHECK(!completed());
102 192
103 // We will be removed; maintain a reference to ensure we won't be deleted 193 // We will be removed; maintain a reference to ensure we won't be deleted
104 // until we're done. 194 // until we're done.
105 scoped_refptr<TrackedCallback> thiz = this; 195 scoped_refptr<TrackedCallback> thiz = this;
106 completed_ = true; 196 completed_ = true;
107 tracker_->Remove(thiz); 197 // We may not have a valid resource, in which case we're not in the tracker.
198 if (resource_id_)
199 tracker_->Remove(thiz);
108 tracker_ = NULL; 200 tracker_ = NULL;
109 } 201 }
110 202
111 } // namespace ppapi 203 } // namespace ppapi
OLDNEW
« no previous file with comments | « ppapi/shared_impl/tracked_callback.h ('k') | ppapi/tests/test_broker.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698