File: mojo_watcher.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (202 lines) | stat: -rw-r--r-- 7,373 bytes parent folder | download | duplicates (10)
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