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
|
// Copyright 2015 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_INTERFACE_ENDPOINT_CLIENT_H_
#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ENDPOINT_CLIENT_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <optional>
#include <string_view>
#include <utility>
#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/dcheck_is_on.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/memory/raw_span.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "mojo/public/cpp/bindings/connection_error_callback.h"
#include "mojo/public/cpp/bindings/connection_group.h"
#include "mojo/public/cpp/bindings/disconnect_reason.h"
#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/message_dispatcher.h"
#include "mojo/public/cpp/bindings/message_metadata_helpers.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/bindings/thread_safe_proxy.h"
namespace mojo {
class AssociatedGroup;
class InterfaceEndpointController;
// InterfaceEndpointClient handles message sending and receiving of an interface
// endpoint, either the implementation side or the client side.
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient
: public MessageReceiverWithResponder {
public:
// Constructs a new InterfaceEndpointClient for use on `task_runner`. Unless
// otherwise noted, all methods (including the destructor) must be called on
// `task_runner`. This does not need to run tasks on the same sequence that
// called the constructor.
//
// `receiver` may be null, but if non-null it must outlive this object.
InterfaceEndpointClient(ScopedInterfaceEndpointHandle handle,
MessageReceiverWithResponderStatus* receiver,
std::unique_ptr<MessageReceiver> payload_validator,
base::span<const uint32_t> sync_method_ordinals,
scoped_refptr<base::SequencedTaskRunner> task_runner,
uint32_t interface_version,
const char* interface_name,
MessageToMethodInfoCallback method_info_callback,
MessageToMethodNameCallback method_name_callback);
InterfaceEndpointClient(const InterfaceEndpointClient&) = delete;
InterfaceEndpointClient& operator=(const InterfaceEndpointClient&) = delete;
~InterfaceEndpointClient() override;
// Sets the error handler to receive notifications when an error is
// encountered.
void set_connection_error_handler(base::OnceClosure error_handler) {
CHECK(sequence_checker_.CalledOnValidSequence());
error_handler_ = std::move(error_handler);
error_with_reason_handler_.Reset();
}
void set_connection_error_with_reason_handler(
ConnectionErrorWithReasonCallback error_handler) {
CHECK(sequence_checker_.CalledOnValidSequence());
error_with_reason_handler_ = std::move(error_handler);
error_handler_.Reset();
}
// Returns true if an error was encountered.
bool encountered_error() const {
CHECK(sequence_checker_.CalledOnValidSequence());
return encountered_error_;
}
// Returns true if this endpoint has any pending callbacks.
bool has_pending_responders() const {
CHECK(sequence_checker_.CalledOnValidSequence());
base::AutoLock lock(async_responders_lock_);
return !async_responders_.empty() || !sync_responses_.empty();
}
AssociatedGroup* associated_group();
scoped_refptr<ThreadSafeProxy> CreateThreadSafeProxy(
scoped_refptr<ThreadSafeProxy::Target> target,
const base::Location& location);
// Sets a MessageFilter which can filter a message after validation but
// before dispatch.
void SetFilter(std::unique_ptr<MessageFilter> filter);
// After this call the object is in an invalid state and shouldn't be reused.
ScopedInterfaceEndpointHandle PassHandle();
// Raises an error on the underlying message pipe. It disconnects the pipe
// and notifies all interfaces running on this pipe.
void RaiseError();
void CloseWithReason(uint32_t custom_reason, std::string_view description);
// Used by ControlMessageProxy to send messages through this endpoint.
void SendControlMessage(Message* message);
void SendControlMessageWithResponder(
Message* message,
std::unique_ptr<MessageReceiver> responder);
// MessageReceiverWithResponder implementation:
// They must only be called when the handle is not in pending association
// state.
bool PrefersSerializedMessages() override;
bool Accept(Message* message) override;
bool AcceptWithResponder(Message* message,
std::unique_ptr<MessageReceiver> responder) override;
// Controls how sync messages are forwarded.
enum class SyncSendMode {
// Allows the InterfaceEndpointClient to do its own internal sync wait when
// sending a sync message. Used in the common case where the reply is waited
// upon from the InterfaceEndpointClient's bound sequence.
kAllowSyncWait,
// Forces the InterfaceEndpointClient to send a sync message as if it were
// async, leaving any waiting up to the caller.
kForceAsync,
};
// Implementations used by both SendControlMessage* and Accept* above.
bool SendMessage(Message* message, bool is_control_message);
bool SendMessageWithResponder(Message* message,
bool is_control_message,
SyncSendMode sync_send_mode,
std::unique_ptr<MessageReceiver> responder);
// The following methods are called by the router. They must be called
// outside of the router's lock.
// NOTE: |message| must have passed message header validation.
bool HandleIncomingMessage(Message* message);
void NotifyError(const std::optional<DisconnectReason>& reason);
// The following methods send interface control messages.
// They must only be called when the handle is not in pending association
// state.
void QueryVersion(base::OnceCallback<void(uint32_t)> callback);
void RequireVersion(uint32_t version);
void FlushForTesting();
void FlushAsyncForTesting(base::OnceClosure callback);
// Sets a callback to handle idle notifications. This callback will be invoked
// any time the peer endpoint sends a NotifyIdle control message AND
// |num_unacked_messages_| is zero.
//
// Configures the peer endpoint to ack incoming messages send NotifyIdle
// notifications only once it's been idle continuously for at least a duration
// of |timeout|.
void SetIdleHandler(base::TimeDelta timeout, base::RepeatingClosure handler);
unsigned int GetNumUnackedMessagesForTesting() const {
return num_unacked_messages_;
}
// Sets a callback to invoke whenever this endpoint receives an
// EnableIdleTracking message from its peer. The callback is invoked with a
// new ConnectionGroup Ref that is expected to be adopted by whatever owns
// this endpoint.
using IdleTrackingEnabledCallback =
base::OnceCallback<void(ConnectionGroup::Ref connection_group)>;
void SetIdleTrackingEnabledCallback(IdleTrackingEnabledCallback callback);
// Called by the ControlMessageHandler when receiving corresponding control
// messages.
bool AcceptEnableIdleTracking(base::TimeDelta timeout);
bool AcceptMessageAck();
bool AcceptNotifyIdle();
void MaybeStartIdleTimer();
void MaybeSendNotifyIdle();
const char* interface_name() const { return interface_name_; }
MessageToMethodInfoCallback method_info_callback() const {
return method_info_callback_;
}
MessageToMethodNameCallback method_name_callback() const {
return method_name_callback_;
}
#if DCHECK_IS_ON()
void SetNextCallLocation(const base::Location& location) {
next_call_location_ = location;
}
#endif
// This allows the endpoint to be reset from a sequence other than the one on
// which it was bound. This should only be used with caution, and it is
// critical that the calling sequence cannot run tasks concurrently with the
// bound sequence. There's no practical way for this to be asserted, so we
// have to take your word for it. If this constraint is not upheld, there will
// be data races internal to the bindings object which can lead to UAFs or
// surprise message dispatches.
void ResetFromAnotherSequenceUnsafe();
// Tells the client to forget about a pending async request for which it still
// hasn't seen a response. Called by the router, possibly from other threads.
// The router lock must be held when calling this.
void ForgetAsyncRequest(uint64_t request_id);
base::span<const uint32_t> sync_method_ordinals() const {
return sync_method_ordinals_;
}
// If adding a new call to this function, be sure to update the list of
// suffixes in histograms.xml
static void SetThreadNameSuffixForMetrics(std::string thread_name);
private:
struct PendingAsyncResponse {
public:
PendingAsyncResponse(uint32_t request_message_name,
std::unique_ptr<MessageReceiver> responder);
PendingAsyncResponse(PendingAsyncResponse&&);
PendingAsyncResponse(const PendingAsyncResponse&) = delete;
PendingAsyncResponse& operator=(PendingAsyncResponse&&);
PendingAsyncResponse& operator=(const PendingAsyncResponse&) = delete;
~PendingAsyncResponse();
uint32_t request_message_name;
std::unique_ptr<MessageReceiver> responder;
};
using AsyncResponderMap = std::map<uint64_t, PendingAsyncResponse>;
struct SyncResponseInfo {
public:
SyncResponseInfo(uint32_t request_message_name, bool* in_response_received);
SyncResponseInfo(const SyncResponseInfo&) = delete;
SyncResponseInfo& operator=(const SyncResponseInfo&) = delete;
~SyncResponseInfo();
uint32_t request_message_name;
Message response;
// Points to a stack-allocated variable.
raw_ptr<bool> response_received;
};
using SyncResponseMap = std::map<uint64_t, std::unique_ptr<SyncResponseInfo>>;
// Used as the sink for |payload_validator_| and forwards messages to
// HandleValidatedMessage().
class HandleIncomingMessageThunk : public MessageReceiver {
public:
explicit HandleIncomingMessageThunk(InterfaceEndpointClient* owner);
HandleIncomingMessageThunk(const HandleIncomingMessageThunk&) = delete;
HandleIncomingMessageThunk& operator=(const HandleIncomingMessageThunk&) =
delete;
~HandleIncomingMessageThunk() override;
// MessageReceiver implementation:
bool Accept(Message* message) override;
private:
// RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
// speedometer3).
RAW_PTR_EXCLUSION InterfaceEndpointClient* const owner_ = nullptr;
};
void InitControllerIfNecessary();
void OnAssociationEvent(
ScopedInterfaceEndpointHandle::AssociationEvent event);
bool HandleValidatedMessage(Message* message);
const base::raw_span<const uint32_t> sync_method_ordinals_;
// The callback to invoke when our peer endpoint sends us NotifyIdle and we
// have no outstanding unacked messages. If null, no callback has been set and
// we do not expect to receive NotifyIdle or MessageAck messages from the
// peer.
base::RepeatingClosure idle_handler_;
// A callback to invoke if and when this endpoint receives an
// EnableIdleTracking control message.
IdleTrackingEnabledCallback idle_tracking_enabled_callback_;
// The timeout to wait for continuous idling before notiftying our peer that
// we're idle.
std::optional<base::TimeDelta> idle_timeout_;
// The current idle timer, valid only while we're idle. If this fires, we send
// a NotifyIdle to our peer.
std::optional<base::OneShotTimer> notify_idle_timer_;
// A ref to a ConnectionGroup used to track the idle state of this endpoint,
// if any. Only non-null if an EnableIdleTracking message has been received.
// This is a weak ref to the group.
ConnectionGroup::Ref idle_tracking_connection_group_;
// Indicates the number of unacked messages that have been sent so far. Only
// non-zero when |idle_handler_| has been set and some number of unacked
// messages remain in-flight.
unsigned int num_unacked_messages_ = 0;
ScopedInterfaceEndpointHandle handle_;
std::unique_ptr<AssociatedGroup> associated_group_;
// RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling
// profiler data).
RAW_PTR_EXCLUSION InterfaceEndpointController* controller_ = nullptr;
// RAW_PTR_EXCLUSION: Performance reasons (based on analysis of sampling
// profiler data).
RAW_PTR_EXCLUSION MessageReceiverWithResponderStatus* const
incoming_receiver_ = nullptr;
HandleIncomingMessageThunk thunk_{this};
MessageDispatcher dispatcher_;
mutable base::Lock async_responders_lock_;
AsyncResponderMap async_responders_ GUARDED_BY(async_responders_lock_);
SyncResponseMap sync_responses_;
uint64_t next_request_id_ = 1;
base::OnceClosure error_handler_;
ConnectionErrorWithReasonCallback error_with_reason_handler_;
bool encountered_error_ = false;
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
internal::ControlMessageProxy control_message_proxy_{this};
internal::ControlMessageHandler control_message_handler_;
const char* const interface_name_;
const MessageToMethodInfoCallback method_info_callback_;
const MessageToMethodNameCallback method_name_callback_;
#if DCHECK_IS_ON()
// The code location of the the most recent call into a method on this
// interface endpoint. This is set *after* the call but *before* any message
// is actually transmitted for it.
base::Location next_call_location_;
#endif
// We use SequenceCheckerImpl directly, to assert some sequence checks even in
// release builds. See https://crbug.com/1325096.
base::SequenceCheckerImpl sequence_checker_;
base::WeakPtrFactory<InterfaceEndpointClient> weak_ptr_factory_{this};
};
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_ENDPOINT_CLIENT_H_
|