File: remote.h

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; 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 (409 lines) | stat: -rw-r--r-- 18,319 bytes parent folder | download | duplicates (5)
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_PUBLIC_CPP_BINDINGS_REMOTE_H_
#define MOJO_PUBLIC_CPP_BINDINGS_REMOTE_H_

#include <cstdint>
#include <tuple>
#include <utility>

#include "base/check.h"
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "mojo/public/cpp/bindings/async_flusher.h"
#include "mojo/public/cpp/bindings/lib/interface_ptr_state.h"
#include "mojo/public/cpp/bindings/pending_flush.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/runtime_features.h"
#include "mojo/public/cpp/system/message_pipe.h"

namespace mojo {

// A Remote is used to issue Interface method calls to a single connected
// Receiver or PendingReceiver. The Remote must be bound in order to issue those
// method calls, and it becomes bound by consuming a PendingRemote either at
// construction time or by calling |Bind()|.
//
// Remote is NOT thread- or sequence-safe and must be used on a single
// (but otherwise arbitrary) sequence. All bound Remote objects are associated
// with a base::SequenceTaskRunner which the Remote uses exclusively to schedule
// response callbacks and disconnection notifications.
//
// The most common ways to bind a Remote are to consume a PendingRemote received
// via some IPC, or to call |BindNewPipeAndPassReceiver()| and send the returned
// PendingReceiver somewhere useful (i.e., to a remote Receiver who will consume
// it). For example:
//
//     mojo::Remote<mojom::Widget> widget;
//     widget_factory->CreateWidget(widget.BindNewPipeAndPassReceiver());
//     widget->Click();
//
// IMPORTANT: There are some things to be aware of regarding Interface method
// calls as they relate to Remote object lifetime:
//
//   - Interface method calls issued immediately before the destruction of a
//     Remote ARE guaranteed to be transmitted to the remote's receiver as long
//     as the receiver itself remains alive, either as a Receiver or a
//     PendingReceiver.
//
//   - In the name of memory safety, Interface method response callbacks (and in
//     general ANY tasks which can be scheduled by a Remote) will NEVER
//     be dispatched beyond the lifetime of the Remote object. As such, if
//     you make a call and you need its reply, you must keep the Remote alive
//     until the reply is received.
template <typename Interface>
class Remote {
 public:
  using InterfaceType = Interface;
  using PendingType = PendingRemote<Interface>;

  // Constructs an unbound Remote. This object cannot issue Interface method
  // calls and does not schedule any tasks.
  Remote() = default;
  Remote(Remote&& other) noexcept { *this = std::move(other); }

  // Constructs a new Remote which is bound from |pending_remote| and which
  // schedules response callbacks and disconnection notifications on the default
  // SequencedTaskRunner (i.e., base::SequencedTaskRunner::GetCurrentDefault()
  // at construction time).
  explicit Remote(PendingRemote<Interface> pending_remote)
      : Remote(std::move(pending_remote), nullptr) {}

  // Constructs a new Remote which is bound from |pending_remote| and which
  // schedules response callbacks and disconnection notifications on
  // |task_runner|. |task_runner| must run tasks on the same sequence that owns
  // this Remote.
  Remote(PendingRemote<Interface> pending_remote,
         scoped_refptr<base::SequencedTaskRunner> task_runner) {
    Bind(std::move(pending_remote), std::move(task_runner));
  }

  Remote(const Remote&) = delete;
  Remote& operator=(const Remote&) = delete;

  ~Remote() = default;

  Remote& operator=(Remote&& other) noexcept {
    internal_state_.Swap(&other.internal_state_);
    return *this;
  }

  // Exposes access to callable Interface methods directed at this Remote's
  // receiver. Must only be called on a bound Remote.
  typename Interface::Proxy_* get() const {
    DCHECK(is_bound())
        << "Cannot issue Interface method calls on an unbound Remote";
    return internal_state_.instance();
  }

  // Shorthand form of |get()|. See above.
  typename Interface::Proxy_* operator->() const { return get(); }
  typename Interface::Proxy_& operator*() const { return *get(); }

  // Indicates whether this Remote is bound and thus can issue Interface method
  // calls via the above accessors.
  //
  // NOTE: The state of being "bound" should not be confused with the state of
  // being "connected" (see |is_connected()| below). A Remote is NEVER passively
  // unbound and the only way for it to become unbound is to explicitly call
  // |reset()| or |Unbind()|. As such, unless you make explicit calls to those
  // methods, it is always safe to assume that a Remote you've bound will remain
  // bound and callable.
  bool is_bound() const { return internal_state_.is_bound(); }
  explicit operator bool() const { return is_bound(); }

  // Indicates whether this Remote is connected to a receiver. Must only be
  // called on a bound Remote. If this returns |true|, method calls made by this
  // Remote may eventually end up at the connected receiver (though it's of
  // course possible for this call to race with disconnection). If this returns
  // |false| however, all future Interface method calls on this Remote will be
  // silently dropped.
  //
  // A bound Remote becomes disconnected automatically either when its receiver
  // is destroyed, or when it receives a malformed or otherwise unexpected
  // response message from the receiver.
  //
  // NOTE: The state of being "bound" should not be confused with the state of
  // being "connected". See |is_bound()| above.
  bool is_connected() const {
    DCHECK(is_bound());
    return !internal_state_.encountered_error();
  }

  // Sets a Closure to be invoked if this Remote is cut off from its receiver.
  // This can happen if the corresponding Receiver (or unconsumed
  // PendingReceiver) is destroyed, or if the Receiver sends a malformed or
  // otherwise unexpected response message to this Remote. Must only be called
  // on a bound Remote object, and only remains set as long as the Remote is
  // both bound and connected.
  //
  // If invoked at all, |handler| will be scheduled asynchronously using the
  // Remote's bound SequencedTaskRunner.
  void set_disconnect_handler(base::OnceClosure handler) {
    if (is_connected())
      internal_state_.set_connection_error_handler(std::move(handler));
  }

  // Like above but also receives extra user-defined metadata about why the
  // receiving endpoint was closed.
  void set_disconnect_with_reason_handler(
      ConnectionErrorWithReasonCallback handler) {
    internal_state_.set_connection_error_with_reason_handler(
        std::move(handler));
  }

  // A convenient helper that resets this Remote on disconnect. Note that this
  // replaces any previously set disconnection handler. Must be called on a
  // bound Remote object. If the Remote is connected, a callback is set to reset
  // it after it is disconnected. If Remote is bound but disconnected then reset
  // is called immediately.
  void reset_on_disconnect() {
    if (!is_connected()) {
      reset();
      return;
    }
    set_disconnect_handler(
        base::BindOnce(&Remote::reset, base::Unretained(this)));
  }

  // Sets a Closure to be invoked any time the receiving endpoint reports itself
  // as idle and there are no in-flight messages it has yet to acknowledge, and
  // this state occurs continuously for a duration of at least |timeout|. The
  // first time this is called, it must be called BEFORE sending any interface
  // messages to the receiver. It may be called any number of times after that
  // to reconfigure the idle timeout period or assign a new idle handler.
  //
  // Once called, the interface connection incurs some permanent additional
  // per-message overhead to help track idle state across the interface
  // boundary.
  //
  // Whenever this callback is invoked, the following conditions are guaranteed
  // to hold:
  //
  //   - There are no messages sent on this Remote that have not already been
  //     dispatched by the receiver.
  //   - There are no interfaces which were bound directly or transitively
  //     through this Remote and are still connected.
  //   - The receiver has explicitly notified us that it considers itself to be
  //     "idle."
  //   - The receiver has not dispatched any additional messages since sending
  //     this idle notification.
  //   - The Remote does not have any outstanding reply callbacks that haven't
  //     been called yet.
  //   - All of the above has been true continuously for a duration of at least
  //     |timeout|.
  //
  void set_idle_handler(base::TimeDelta timeout,
                        base::RepeatingClosure handler) {
    internal_state_.set_idle_handler(timeout, std::move(handler));
  }

  // A convenient helper for common idle timeout behavior. This is equivalent to
  // calling |set_idle_handler| with a handler that only resets this Remote.
  void reset_on_idle_timeout(base::TimeDelta timeout) {
    set_idle_handler(
        timeout, base::BindRepeating(&Remote::reset, base::Unretained(this)));
  }

  // Resets this Remote to an unbound state. To reset the Remote and recover an
  // PendingRemote that can be bound again later, use |Unbind()| instead.
  void reset() {
    State doomed_state;
    internal_state_.Swap(&doomed_state);
  }

  // Similar to the method above, but also specifies a disconnect reason.
  void ResetWithReason(uint32_t custom_reason, const std::string& description) {
    if (internal_state_.is_bound())
      internal_state_.CloseWithReason(custom_reason, description);
    reset();
  }

  // Returns the version of Interface used by this Remote. Defaults to 0 but can
  // be adjusted either at binding time, or by invoking either |QueryVersion()|
  // or |RequireVersion()|.
  uint32_t version() const { return internal_state_.version(); }

  // Binds this Remote, connecting it to a new PendingReceiver which is
  // returned for transmission to some Receiver which can bind it. The Remote
  // will schedule any response callbacks or disconnection notifications on the
  // default SequencedTaskRunner (i.e.
  // base::SequencedTaskRunner::GetCurrentDefault() at the time of this call).
  // Must only be called on an unbound Remote.
  [[nodiscard]] PendingReceiver<Interface> BindNewPipeAndPassReceiver() {
    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
                        << " is already bound";
    return BindNewPipeAndPassReceiver(nullptr);
  }

  // Like above, but the Remote will schedule response callbacks and
  // disconnection notifications on |task_runner| instead of the default
  // SequencedTaskRunner. |task_runner| must run tasks on the same sequence that
  // owns this Remote.
  [[nodiscard]] PendingReceiver<Interface> BindNewPipeAndPassReceiver(
      scoped_refptr<base::SequencedTaskRunner> task_runner) {
    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
                        << " is already bound";
    if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) {
      reset();
      return PendingReceiver<Interface>();
    }
    MessagePipe pipe;
    Bind(PendingRemote<Interface>(std::move(pipe.handle0), 0),
         std::move(task_runner));
    return PendingReceiver<Interface>(std::move(pipe.handle1));
  }

  // Binds this Remote by consuming |pending_remote|, which must be valid. The
  // Remote will schedule any response callbacks or disconnection notifications
  // on the default SequencedTaskRunner (i.e.
  // base::SequencedTaskRunner::GetCurrentDefault() at the time of this call).
  // Must only be called on an unbound Remote.
  void Bind(PendingRemote<Interface> pending_remote) {
    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
                        << " is already bound";
    DCHECK(pending_remote.is_valid());
    Bind(std::move(pending_remote), nullptr);
  }

  // Like above, but the Remote will schedule response callbacks and
  // disconnection notifications on |task_runner| instead of the default
  // SequencedTaskRunner. Must only be called on an unbound Remote.
  // |task_runner| must run tasks on the same sequence that owns this Remote.
  void Bind(PendingRemote<Interface> pending_remote,
            scoped_refptr<base::SequencedTaskRunner> task_runner) {
    DCHECK(!is_bound()) << "Remote for " << Interface::Name_
                        << " is already bound";
    if (!pending_remote) {
      reset();
      return;
    }
    if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) {
      reset();
      return;
    }
    internal_state_.Bind(pending_remote.internal_state(),
                         std::move(task_runner));

    // Force the internal state to configure its proxy. Unlike InterfacePtr we
    // do not use Remote in transit, so binding to a pipe handle can also imply
    // binding to a SequencedTaskRunner and observing pipe handle state. This
    // allows for e.g. |is_connected()| to be a more reliable API than
    // |InterfacePtr::encountered_error()|.
    std::ignore = internal_state_.instance();
  }

  // Unbinds this Remote, rendering it unable to issue further Interface method
  // calls. Returns a PendingRemote which may be passed across threads or
  // processes and consumed by another Remote elsewhere.
  //
  // Note that it is an error (the bad, crashy kind of error) to attempt to
  // |Unbind()| a Remote which is awaiting one or more responses to previously
  // issued Interface method calls. Calling this method should only be
  // considered in cases where satisfaction of that constraint can be proven.
  //
  // Must only be called on a bound Remote.
  [[nodiscard]] PendingRemote<Interface> Unbind() {
    DCHECK(is_bound());
    CHECK(!internal_state_.has_pending_callbacks());
    State state;
    internal_state_.Swap(&state);
    internal::PendingRemoteState pending_state = state.Unbind();
    return PendingRemote<Interface>(std::move(pending_state.pipe),
                                    pending_state.version);
  }

  // Queries the max version that the receiving endpoint supports. Once a
  // response is received, |callback| will be invoked with the version number
  // and the version number of this Remote object will also be updated.
  void QueryVersion(base::OnceCallback<void(uint32_t)> callback) {
    internal_state_.QueryVersion(std::move(callback));
  }

  // Requires the receiving endpoint to support at least the specified
  // |version|. If it does not, it will close its end of the connection
  // immediately.
  void RequireVersion(uint32_t version) {
    internal_state_.RequireVersion(version);
  }

  // Pauses the receiving endpoint until the flush corresponding to |flush| has
  // completed. Any calls made on this Remote prior to this call will be
  // dispatched at the receiving endpoint before pausing. The endpoint will not
  // dispatch any subsequent calls until the flush operation corresponding to
  // |flush| has been completed or canceled.
  //
  // See documentation for |FlushAsync()| on Remote and Receiver for how to
  // acquire a PendingFlush object, and documentation on PendingFlush for
  // example usage.
  void PauseReceiverUntilFlushCompletes(PendingFlush flush) {
    internal_state_.PauseReceiverUntilFlushCompletes(std::move(flush));
  }

  // Flushes the receiving endpoint asynchronously using |flusher|. Once all
  // calls made on this Remote prior to this |FlushAsyncWithFlusher()| call have
  // dispatched at the receiving endpoint, |flusher| will signal its
  // corresponding PendingFlush, unblocking any endpoint waiting on the flush
  // operation.
  //
  // NOTE: It is more common to use |FlushAsync()| defined below. If you really
  // want to provide your own AsyncFlusher using this method, see the
  // single-arugment constructor on PendingFlush. This would typically be used
  // when code executing on the current sequence wishes to immediately pause
  // one of its remote endpoints to wait on a flush operation that needs to be
  // initiated on a separate sequence. Rather than bouncing to the second
  // sequence to initiate a flush and then passing a PendingFlush back to the
  // original sequence, the AsyncFlusher/PendingFlush can be created on the
  // original sequence and a single task can be posted to pass the AsyncFlusher
  // to the second sequence for use with this method.
  void FlushAsyncWithFlusher(AsyncFlusher flusher) {
    internal_state_.FlushAsync(std::move(flusher));
  }

  // Same as above but an AsyncFlusher/PendingFlush pair is created on the
  // caller's behalf. The AsyncFlusher is immediately passed to a
  // |FlushAsyncWithFlusher()| call on this object, while the PendingFlush is
  // returned for use by the caller. See documentation on PendingFlush for
  // example usage.
  PendingFlush FlushAsync() {
    AsyncFlusher flusher;
    PendingFlush flush(&flusher);
    FlushAsyncWithFlusher(std::move(flusher));
    return flush;
  }

  // Sends a no-op message on the underlying message pipe and runs the current
  // message loop until its response is received. This can be used in tests to
  // verify that no message was sent on a message pipe in response to some
  // stimulus.
  void FlushForTesting() { internal_state_.FlushForTesting(); }

  // Same as |FlushForTesting()| but will call |callback| when the flush is
  // complete.
  void FlushAsyncForTesting(base::OnceClosure callback) {
    internal_state_.FlushAsyncForTesting(std::move(callback));
  }

  // Returns the number of unacknowledged messages sent by this Remote. Only
  // non-zero when |set_idle_handler()| has been called.
  unsigned int GetNumUnackedMessagesForTesting() const {
    return internal_state_.GetNumUnackedMessagesForTesting();
  }

  // DO NOT USE. Exposed only for internal use and for testing.
  internal::InterfacePtrState<Interface>* internal_state() {
    return &internal_state_;
  }

 private:
  using State = internal::InterfacePtrState<Interface>;
  mutable State internal_state_;
};

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_REMOTE_H_