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
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PPAPI_SHARED_IMPL_TRACKED_CALLBACK_H_
#define PPAPI_SHARED_IMPL_TRACKED_CALLBACK_H_
#include <stdint.h>
#include <memory>
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_resource.h"
#include "ppapi/shared_impl/ppapi_shared_export.h"
#include "ppapi/shared_impl/ppb_message_loop_shared.h"
namespace ppapi {
class CallbackTracker;
class MessageLoopShared;
class Resource;
namespace thunk {
namespace subtle {
// For a friend declaration below.
class EnterBase;
}
}
// |TrackedCallback| represents a tracked Pepper callback (from the browser to
// the plugin), typically still pending. Such callbacks have the standard Pepper
// callback semantics. Execution (i.e., completion) of callbacks happens through
// objects of subclasses of |TrackedCallback|. Two things are ensured: (1) that
// the callback is executed at most once, and (2) once a callback is marked to
// be aborted, any subsequent completion is abortive (even if a non-abortive
// completion had previously been scheduled).
//
// The details of non-abortive completion depend on the type of callback (e.g.,
// different parameters may be required), but basic abort functionality is core.
// The ability to post aborts is needed in many situations to ensure that the
// plugin is not re-entered into. (Note that posting a task to just run
// |Abort()| is different and not correct; calling |PostAbort()| additionally
// guarantees that all subsequent completions will be abortive.)
//
// This class is reference counted so that different things can hang on to it,
// and not worry too much about ensuring Pepper callback semantics. Note that
// the "owning" |CallbackTracker| will keep a reference until the callback is
// completed.
//
// A note on threading:
// TrackedCallback is usable on any thread. It is *mostly* only used when
// ppapi::ProxyLock is held. However, it's necessary that Run() can be called
// without the ProxyLock. This is used to allow running the callback from
// the IO thread. In particular, blocking callbacks may not have a message loop
// to which we could post, so Run() must be able to signal the condition
// variable to wake up the thread that's waiting on the blocking callback, and
// Run() must be able to do this while not holding the ProxyLock.
class PPAPI_SHARED_EXPORT TrackedCallback
: public base::RefCountedThreadSafe<TrackedCallback> {
public:
TrackedCallback() = delete;
// Create a tracked completion callback and register it with the tracker. The
// resource pointer is not stored. If |resource| is NULL, this callback will
// not be added to the callback tracker.
TrackedCallback(Resource* resource, const PP_CompletionCallback& callback);
TrackedCallback(const TrackedCallback&) = delete;
TrackedCallback& operator=(const TrackedCallback&) = delete;
// These run the callback in an abortive manner, or post a task to do so (but
// immediately marking the callback as to be aborted).
void Abort();
void PostAbort();
// Run the callback with the given result. If the callback had previously been
// marked as to be aborted (by |PostAbort()|), |result| will be ignored and
// the callback will be run with result |PP_ERROR_ABORTED|.
//
// Run() will invoke the call immediately, if invoked from the target thread
// (as determined by target_loop_). If invoked on a different thread, the
// callback will be scheduled to run later on target_loop_.
void Run(int32_t result);
void AcquireProxyLockAndRun(int32_t result);
// PostRun is like Run(), except it guarantees that the callback will be run
// later. In particular, if you invoke PostRun on the same thread on which the
// callback is targeted to run, it will *not* be run immediately.
void PostRun(int32_t result);
// A task to perform cleanup or write output parameters before the callback
// returns a result to the plugin. The |result| parameter has the result so
// far, e.g. whether the callback has been aborted. If the callback hasn't
// been aborted the return value of the task will become the callback result.
// The task is always called on the same thread as the callback to the plugin.
using CompletionTask = base::OnceCallback<int32_t(int32_t /* result */)>;
// Sets a task that is run just before calling back into the plugin. This
// should only be called once. Note that the CompletionTask always runs while
// holding the ppapi::ProxyLock.
void set_completion_task(CompletionTask completion_task);
// Returns the ID of the resource which "owns" the callback, or 0 if the
// callback is not associated with any resource.
PP_Resource resource_id() const { return resource_id_; }
// Returns true if this is a blocking callback.
bool is_blocking() const {
// This is set on construction and never changes after that, so there is
// no need to lock.
return !callback_.func;
}
MessageLoopShared* target_loop() const {
// This is set on construction and never changes after that, so there is
// no need to lock.
return target_loop_.get();
}
// Determines if the given callback is pending. A callback is pending if it
// has not completed and has not been aborted. When receiving a plugin call,
// use this to detect if |callback| represents an operation in progress. When
// finishing a plugin call, use this to determine whether to write 'out'
// params and Run |callback|.
// NOTE: an aborted callback has not necessarily completed, so a false result
// doesn't imply that the callback has completed.
// As a convenience, if |callback| is null, this returns false.
static bool IsPending(const scoped_refptr<TrackedCallback>& callback);
// Helper to determine if the given callback is scheduled to run on another
// message loop.
static bool IsScheduledToRun(const scoped_refptr<TrackedCallback>& callback);
private:
bool is_required() {
return (callback_.func &&
!(callback_.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL));
}
bool has_null_target_loop() const { return target_loop_.get() == NULL; }
// Same as PostRun(), but lock_ must already be held.
void PostRunWithLock(int32_t result);
void SignalBlockingCallback(int32_t result);
// TrackedCallback and EnterBase work together to provide appropriate behavior
// for callbacks. Pepper interface implementations and proxies should
// usually not have to check whether callbacks are required, optional, or
// blocking. Nor should interface and proxy implementations have to worry
// about blocking on a callback or marking them complete explicitly.
//
// (There are exceptions; e.g. FileIO checks is_blocking() in order to do
// some operations directly on the calling thread if possible.)
friend class ppapi::thunk::subtle::EnterBase;
// Block until the associated operation has completed. Returns the result.
// This must only be called on a non-main thread on a blocking callback.
int32_t BlockUntilComplete();
// Mark this object as complete and remove it from the tracker. This must only
// be called once. Note that running this may result in this object being
// deleted (so keep a reference if it'll still be needed).
void MarkAsCompleted();
void MarkAsCompletedWithLock();
// This class is ref counted.
friend class base::RefCountedThreadSafe<TrackedCallback>;
~TrackedCallback();
mutable base::Lock lock_;
// Flag used by |PostAbort()| and |PostRun()| to check that we don't schedule
// the callback more than once.
bool is_scheduled_;
scoped_refptr<CallbackTracker> tracker_;
PP_Resource resource_id_;
bool completed_;
bool aborted_;
PP_CompletionCallback callback_;
// Task to run just before calling back into the plugin.
CompletionTask completion_task_;
// The MessageLoopShared on which this callback should be run. This will be
// NULL if we're in-process.
scoped_refptr<MessageLoopShared> target_loop_;
int32_t result_for_blocked_callback_;
// Used for pausing/waking the blocked thread if this is a blocking completion
// callback. Note that in-process, there is no lock, blocking callbacks are
// not allowed, and therefore this pointer will be NULL.
std::unique_ptr<base::ConditionVariable> operation_completed_condvar_;
};
} // namespace ppapi
#endif // PPAPI_SHARED_IMPL_TRACKED_CALLBACK_H_
|