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

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

Issue 10909244: PPAPI: Get TrackedCallback ready for running on non-main threads. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge Created 8 years, 3 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" 11 #include "base/synchronization/lock.h"
12 #include "ppapi/c/dev/ppb_message_loop_dev.h" 12 #include "ppapi/c/dev/ppb_message_loop_dev.h"
13 #include "ppapi/c/pp_completion_callback.h" 13 #include "ppapi/c/pp_completion_callback.h"
14 #include "ppapi/c/pp_errors.h" 14 #include "ppapi/c/pp_errors.h"
15 #include "ppapi/shared_impl/callback_tracker.h" 15 #include "ppapi/shared_impl/callback_tracker.h"
16 #include "ppapi/shared_impl/ppapi_globals.h" 16 #include "ppapi/shared_impl/ppapi_globals.h"
17 #include "ppapi/shared_impl/proxy_lock.h" 17 #include "ppapi/shared_impl/proxy_lock.h"
18 #include "ppapi/shared_impl/resource.h" 18 #include "ppapi/shared_impl/resource.h"
19 19
20 namespace ppapi { 20 namespace ppapi {
21 21
22 // TrackedCallback ------------------------------------------------------------- 22 // TrackedCallback -------------------------------------------------------------
23 23
24 // 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.
25 TrackedCallback::TrackedCallback( 25 TrackedCallback::TrackedCallback(
26 Resource* resource, 26 Resource* resource,
27 const PP_CompletionCallback& callback) 27 const PP_CompletionCallback& callback)
28 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), 28 : is_scheduled_(false),
29 resource_id_(resource ? resource->pp_resource() : 0), 29 resource_id_(resource ? resource->pp_resource() : 0),
30 completed_(false), 30 completed_(false),
31 aborted_(false), 31 aborted_(false),
32 callback_(callback), 32 callback_(callback),
33 result_for_blocked_callback_(PP_OK) { 33 result_for_blocked_callback_(PP_OK) {
34 // We can only track this callback if the resource is valid. It can be NULL
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 34 // TODO(dmichael): Add tracking at the instance level, for callbacks that only
38 // have an instance (e.g. for MouseLock). 35 // have an instance (e.g. for MouseLock).
39 if (resource) { 36 if (resource) {
40 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance( 37 tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance(
41 resource->pp_instance()); 38 resource->pp_instance());
42 tracker_->Add(make_scoped_refptr(this)); 39 tracker_->Add(make_scoped_refptr(this));
43 } 40 }
44 41
45 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); 42 base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock();
46 // We only need a ConditionVariable if the lock is valid (i.e., we're out-of- 43 // We only need a ConditionVariable if the lock is valid (i.e., we're out-of-
47 // process) and the callback is blocking. 44 // process) and the callback is blocking.
48 if (proxy_lock && is_blocking()) 45 if (proxy_lock && is_blocking())
49 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock)); 46 operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock));
50 } 47 }
51 48
52 TrackedCallback::~TrackedCallback() { 49 TrackedCallback::~TrackedCallback() {
53 } 50 }
54 51
55 void TrackedCallback::Abort() { 52 void TrackedCallback::Abort() {
56 if (!completed()) { 53 // It doesn't make sense to abort a callback that's not associated with a
57 aborted_ = true; 54 // resource.
58 Run(PP_ERROR_ABORTED); 55 DCHECK(resource_id_);
59 } 56 Run(PP_ERROR_ABORTED);
60 } 57 }
61 58
62 void TrackedCallback::PostAbort() { 59 void TrackedCallback::PostAbort() {
63 // It doesn't make sense to abort a callback that's not associated with a 60 PostRun(PP_ERROR_ABORTED);
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_) {
70 aborted_ = true;
71 MessageLoop::current()->PostTask(
72 FROM_HERE,
73 RunWhileLocked(base::Bind(&TrackedCallback::Abort,
74 weak_ptr_factory_.GetWeakPtr())));
75 }
76 } 61 }
77 62
78 void TrackedCallback::Run(int32_t result) { 63 void TrackedCallback::Run(int32_t result) {
79 if (!completed()) { 64 // Only allow the callback to be run once. Note that this also covers the case
80 // Cancel any pending calls. 65 // where the callback was previously Aborted because its associated Resource
81 weak_ptr_factory_.InvalidateWeakPtrs(); 66 // went away. The callback may live on for a while because of a reference from
67 // a Closure. But when the Closure runs, Run() quietly does nothing, and the
68 // callback will go away when all referring Closures go away.
69 if (completed())
70 return;
71 if (result == PP_ERROR_ABORTED)
72 aborted_ = true;
82 73
83 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| 74 // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()|
84 // may delete us. 75 // may delete us.
85 PP_CompletionCallback callback = callback_; 76 PP_CompletionCallback callback = callback_;
86 if (aborted()) 77 // Note that this call of Run() may have been scheduled prior to Abort() or
87 result = PP_ERROR_ABORTED; 78 // PostAbort() being called. If we have been told to Abort, that always
79 // trumps a result that was scheduled before.
80 if (aborted())
81 result = PP_ERROR_ABORTED;
88 82
89 if (is_blocking()) { 83 if (is_blocking()) {
90 // If the condition variable is invalid, there are two possibilities. One, 84 // If the condition variable is invalid, there are two possibilities. One,
91 // we're running in-process, in which case the call should have come in on 85 // we're running in-process, in which case the call should have come in on
92 // the main thread and we should have returned PP_ERROR_BLOCKS_MAIN_THREAD 86 // 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 87 // well before this. Otherwise, this callback was not created as a
94 // blocking callback. Either way, there's some internal error. 88 // blocking callback. Either way, there's some internal error.
95 if (!operation_completed_condvar_.get()) { 89 if (!operation_completed_condvar_.get()) {
96 NOTREACHED(); 90 NOTREACHED();
97 return; 91 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 } 92 }
93 result_for_blocked_callback_ = result;
94 // Retain ourselves, since MarkAsCompleted will remove us from the
95 // tracker. Then MarkAsCompleted before waking up the blocked thread,
96 // which could potentially re-enter.
97 scoped_refptr<TrackedCallback> thiz(this);
98 MarkAsCompleted();
99 // Wake up the blocked thread. See BlockUntilComplete for where the thread
100 // Wait()s.
101 operation_completed_condvar_->Signal();
102 } else {
103 // Do this before running the callback in case of reentrancy (which
104 // shouldn't happen, but avoid strange failures).
105 MarkAsCompleted();
106 // TODO(dmichael): Associate a message loop with the callback; if it's not
107 // the same as the current thread's loop, then post it to the right loop.
108 CallWhileUnlocked(PP_RunCompletionCallback, &callback, result);
116 } 109 }
117 } 110 }
118 111
119 void TrackedCallback::PostRun(int32_t result) { 112 void TrackedCallback::PostRun(int32_t result) {
120 DCHECK(!completed()); 113 if (completed()) {
121 if (!completed()) { 114 NOTREACHED();
122 // There should be no pending calls. 115 return;
123 DCHECK(!weak_ptr_factory_.HasWeakPtrs()); 116 }
124 weak_ptr_factory_.InvalidateWeakPtrs(); 117 if (result == PP_ERROR_ABORTED)
118 aborted_ = true;
119 // We might abort when there's already a scheduled callback, but callers
120 // should never try to PostRun more than once otherwise.
121 DCHECK(result == PP_ERROR_ABORTED || !is_scheduled_);
viettrungluu 2012/09/25 00:18:11 Is |is_scheduled_| checked anywhere else?
dmichael (off chromium) 2012/10/24 21:40:52 Nope. I was trying to keep parity with the weak_pt
125 122
126 if (resource_id_) { 123 base::Closure callback_closure(
127 // If it has a resource_id_, it's in the tracker, and may be aborted if 124 RunWhileLocked(base::Bind(&TrackedCallback::Run, this, result)));
128 // the resource is destroyed. 125 MessageLoop::current()->PostTask(FROM_HERE, callback_closure);
129 MessageLoop::current()->PostTask( 126 is_scheduled_ = true;
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 } 127 }
148 128
149 // static 129 // static
150 bool TrackedCallback::IsPending( 130 bool TrackedCallback::IsPending(
151 const scoped_refptr<TrackedCallback>& callback) { 131 const scoped_refptr<TrackedCallback>& callback) {
152 if (!callback.get()) 132 if (!callback.get())
153 return false; 133 return false;
154 return !callback->completed(); 134 return !callback->completed();
155 } 135 }
156 136
157 // static
158 void TrackedCallback::ClearAndRun(scoped_refptr<TrackedCallback>* callback,
159 int32_t result) {
160 scoped_refptr<TrackedCallback> temp;
161 temp.swap(*callback);
162 temp->Run(result);
163 }
164
165 // static
166 void TrackedCallback::ClearAndAbort(scoped_refptr<TrackedCallback>* callback) {
167 scoped_refptr<TrackedCallback> temp;
168 temp.swap(*callback);
169 temp->Abort();
170 }
171
172 int32_t TrackedCallback::BlockUntilComplete() { 137 int32_t TrackedCallback::BlockUntilComplete() {
173 // Note, we are already holding the proxy lock in all these methods, including 138 // 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). 139 // this one (see ppapi/thunk/enter.cc for where it gets acquired).
175 140
176 // It doesn't make sense to wait on a non-blocking callback. Furthermore, 141 // It doesn't make sense to wait on a non-blocking callback. Furthermore,
177 // BlockUntilComplete should never be called for in-process plugins, where 142 // BlockUntilComplete should never be called for in-process plugins, where
178 // blocking callbacks are not supported. 143 // blocking callbacks are not supported.
179 CHECK(operation_completed_condvar_.get()); 144 CHECK(operation_completed_condvar_.get());
180 if (!is_blocking() || !operation_completed_condvar_.get()) { 145 if (!is_blocking() || !operation_completed_condvar_.get()) {
181 NOTREACHED(); 146 NOTREACHED();
(...skipping 12 matching lines...) Expand all
194 // until we're done. 159 // until we're done.
195 scoped_refptr<TrackedCallback> thiz = this; 160 scoped_refptr<TrackedCallback> thiz = this;
196 completed_ = true; 161 completed_ = true;
197 // We may not have a valid resource, in which case we're not in the tracker. 162 // We may not have a valid resource, in which case we're not in the tracker.
198 if (resource_id_) 163 if (resource_id_)
199 tracker_->Remove(thiz); 164 tracker_->Remove(thiz);
200 tracker_ = NULL; 165 tracker_ = NULL;
201 } 166 }
202 167
203 } // namespace ppapi 168 } // namespace ppapi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698