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