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 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/mojo/mojo_watcher.h"
#include "base/task/single_thread_task_runner.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_mojo_handle_signals.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_mojo_watch_callback.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/cross_thread_persistent.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
namespace blink {
// static
MojoWatcher* MojoWatcher::Create(mojo::Handle handle,
const MojoHandleSignals* signals_dict,
V8MojoWatchCallback* callback,
ExecutionContext* context) {
MojoWatcher* watcher = MakeGarbageCollected<MojoWatcher>(context, callback);
MojoResult result = watcher->Watch(handle, signals_dict);
// TODO(alokp): Consider raising an exception.
// Current clients expect to recieve the initial error returned by MojoWatch
// via watch callback.
//
// Note that the usage of WrapPersistent is intentional so that the initial
// error is guaranteed to be reported to the client in case where the given
// handle is invalid and garbage collection happens before the callback
// is scheduled.
if (result != MOJO_RESULT_OK) {
watcher->task_runner_->PostTask(
FROM_HERE, WTF::BindOnce(&V8MojoWatchCallback::InvokeAndReportException,
WrapPersistent(callback),
WrapPersistent(watcher), result));
}
return watcher;
}
MojoWatcher::~MojoWatcher() = default;
MojoResult MojoWatcher::cancel() {
if (!trap_handle_.is_valid())
return MOJO_RESULT_INVALID_ARGUMENT;
trap_handle_.reset();
return MOJO_RESULT_OK;
}
void MojoWatcher::Trace(Visitor* visitor) const {
visitor->Trace(callback_);
ScriptWrappable::Trace(visitor);
ExecutionContextLifecycleObserver::Trace(visitor);
}
bool MojoWatcher::HasPendingActivity() const {
return handle_.is_valid();
}
void MojoWatcher::ContextDestroyed() {
cancel();
}
MojoWatcher::MojoWatcher(ExecutionContext* context,
V8MojoWatchCallback* callback)
: ActiveScriptWrappable<MojoWatcher>({}),
ExecutionContextLifecycleObserver(context),
task_runner_(context->GetTaskRunner(TaskType::kInternalDefault)),
callback_(callback) {}
MojoResult MojoWatcher::Watch(mojo::Handle handle,
const MojoHandleSignals* signals_dict) {
::MojoHandleSignals signals = MOJO_HANDLE_SIGNAL_NONE;
if (signals_dict->readable())
signals |= MOJO_HANDLE_SIGNAL_READABLE;
if (signals_dict->writable())
signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
if (signals_dict->peerClosed())
signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
MojoResult result =
mojo::CreateTrap(&MojoWatcher::OnHandleReady, &trap_handle_);
DCHECK_EQ(MOJO_RESULT_OK, result);
result = MojoAddTrigger(trap_handle_.get().value(), handle.value(), signals,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
reinterpret_cast<uintptr_t>(this), nullptr);
if (result != MOJO_RESULT_OK)
return result;
// If MojoAddTrigger succeeded above, we need this object to stay alive at
// least until OnHandleReady is invoked with MOJO_RESULT_CANCELLED, which
// signals the final invocation by the trap.
keep_alive_ = this;
handle_ = handle;
MojoResult ready_result;
result = Arm(&ready_result);
if (result == MOJO_RESULT_OK)
return result;
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
// We couldn't arm the watcher because the handle is already ready to
// trigger a success notification. Post a notification manually.
task_runner_->PostTask(FROM_HERE,
WTF::BindOnce(&MojoWatcher::RunReadyCallback,
WrapPersistent(this), ready_result));
return MOJO_RESULT_OK;
}
// If MojoAddTrigger succeeds but Arm does not, that means another thread
// closed the watched handle in between. Treat it like we'd treat
// MojoAddTrigger trying to watch an invalid handle.
trap_handle_.reset();
return MOJO_RESULT_INVALID_ARGUMENT;
}
MojoResult MojoWatcher::Arm(MojoResult* ready_result) {
// Nothing to do if the watcher is inactive.
if (!handle_.is_valid())
return MOJO_RESULT_OK;
uint32_t num_blocking_events = 1;
MojoTrapEvent blocking_event = {sizeof(blocking_event)};
MojoResult result = MojoArmTrap(trap_handle_.get().value(), nullptr,
&num_blocking_events, &blocking_event);
if (result == MOJO_RESULT_OK)
return MOJO_RESULT_OK;
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
DCHECK_EQ(1u, num_blocking_events);
DCHECK_EQ(reinterpret_cast<uintptr_t>(this),
blocking_event.trigger_context);
*ready_result = blocking_event.result;
return result;
}
return result;
}
// static
void MojoWatcher::OnHandleReady(const MojoTrapEvent* event) {
// It is safe to assume the MojoWathcer still exists, because we keep it alive
// until we've dispatched MOJO_RESULT_CANCELLED from here to RunReadyCallback,
// and that is always the last notification we'll dispatch.
MojoWatcher* watcher = reinterpret_cast<MojoWatcher*>(event->trigger_context);
PostCrossThreadTask(
*watcher->task_runner_, FROM_HERE,
CrossThreadBindOnce(&MojoWatcher::RunReadyCallback,
WrapCrossThreadWeakPersistent(watcher),
event->result));
}
void MojoWatcher::RunReadyCallback(MojoResult result) {
if (result == MOJO_RESULT_CANCELLED) {
// Last notification.
keep_alive_.Clear();
handle_ = mojo::Handle();
// Only dispatch to the callback if this cancellation was implicit due to
// |handle_| closure. If it was explicit, |trap_handlde_| has already been
// reset.
if (trap_handle_.is_valid()) {
trap_handle_.reset();
callback_->InvokeAndReportException(this, result);
}
return;
}
// Ignore callbacks if not watching.
if (!trap_handle_.is_valid())
return;
callback_->InvokeAndReportException(this, result);
// The user callback may have canceled watching.
if (!trap_handle_.is_valid())
return;
// Rearm the watcher so another notification can fire.
//
// TODO(rockot): MojoWatcher should expose some better approximation of the
// new watcher API, including explicit add and removal of handles from the
// watcher, as well as explicit arming.
MojoResult ready_result;
MojoResult arm_result = Arm(&ready_result);
if (arm_result == MOJO_RESULT_OK)
return;
if (arm_result == MOJO_RESULT_FAILED_PRECONDITION) {
task_runner_->PostTask(
FROM_HERE, WTF::BindOnce(&MojoWatcher::RunReadyCallback,
WrapWeakPersistent(this), ready_result));
return;
}
}
} // namespace blink
|