// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef IPC_IPC_MOJO_BOOTSTRAP_H_ #define IPC_IPC_MOJO_BOOTSTRAP_H_ #include #include #include "base/component_export.h" #include "base/memory/ref_counted.h" #include "base/task/single_thread_task_runner.h" #include "build/build_config.h" #include "ipc/ipc.mojom.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_listener.h" #include "mojo/public/cpp/bindings/associated_group.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/lib/message_quota_checker.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/system/message_pipe.h" namespace IPC { // Incoming legacy IPCs have always been dispatched to one of two threads: the // IO thread (when an installed MessageFilter handles the message), or the // thread which owns the corresponding ChannelProxy receiving the message. There // were no other places to route legacy IPC messages, so when a message arrived // the legacy IPC system would run through its MessageFilters and if the message // was still unhandled, it would be posted to the ChannelProxy thread for // further processing. // // Mojo on the other hand allows for mutually associated endpoints (that is, // endpoints which locally share the same message pipe) to span any number of // threads while still guaranteeing that each endpoint on a given thread // preserves FIFO order of messages dispatched there. This means that if a // message arrives carrying a PendingAssociatedRemote/Receiver endpoint, and // then another message arrives which targets that endpoint, the entire pipe // will be blocked from dispatch until the endpoint is bound: otherwise we have // no idea where to dispatch the message such that we can uphold the FIFO // guarantee between the new endpoint and any other endpoints on the thread it // ends up binding to. // // Channel-associated interfaces share a message pipe with the legacy IPC // Channel, and in order to avoid nasty surprises during the migration process // we decided to constrain how incoming Channel-associated endpoints could be // bound: you must either bind them immediately as they arrive on the IO thread, // or you must immediately post a task to the ChannelProxy thread to bind them. // This allows all aforementioned FIFO guaratees to be upheld without ever // stalling dispatch of legacy IPCs (particularly on the IO thread), because // when we see a message targeting an unbound endpoint we can safely post it to // the ChannelProxy's task runner before forging ahead to dispatch subsequent // messages. No stalling. // // As there are some cases where a Channel-associated endpoint really wants to // receive messages on a different TaskRunner, we want to allow that now. It's // safe as long as the application can guarantee that the endpoint in question // will be bound to a task runner *before* any messages are received for that // endpoint. // // HOWEVER, it turns out that we cannot simply adhere to the application's // wishes when an alternative TaskRunner is provided at binding time: over time // we have accumulated application code which binds Channel-associated endpoints // to task runners which -- while running tasks exclusively on the ChannelProxy // thread -- are not the ChannelProxy's own task runner. Such code now // implicitly relies on the behavior of Channel-associated interfaces always // dispatching their messages to the ChannelProxy task runner. This is tracked // by https://crbug.com/1209188. // // Finally, the point: if you really know you want to bind your endpoint to an // alternative task runner and you can really guarantee that no messages may // have already arrived for it on the IO thread, you can do the binding within // the extent of a ScopedAllowOffSequenceChannelAssociatedBindings. This will // flag the endpoint such that it honors your binding configuration, and its // incoming messages will actually dispatch to the task runner you provide. class COMPONENT_EXPORT(IPC) ScopedAllowOffSequenceChannelAssociatedBindings { public: ScopedAllowOffSequenceChannelAssociatedBindings(); ~ScopedAllowOffSequenceChannelAssociatedBindings(); private: const bool outer_flag_; }; // MojoBootstrap establishes a pair of associated interfaces between two // processes in Chrome. // // Clients should implement MojoBootstrap::Delegate to get the associated pipes // from MojoBootstrap object. // // This lives on IO thread other than Create(), which can be called from // UI thread as Channel::Create() can be. class COMPONENT_EXPORT(IPC) MojoBootstrap { public: virtual ~MojoBootstrap() {} // Create the MojoBootstrap instance, using |handle| as the message pipe, in // mode as specified by |mode|. The result is passed to |delegate|. static std::unique_ptr Create( mojo::ScopedMessagePipeHandle handle, Channel::Mode mode, const scoped_refptr& ipc_task_runner, const scoped_refptr& proxy_task_runner, const scoped_refptr& quota_checker); // Initialize the Channel pipe and interface endpoints. This performs all // setup except actually starting to read messages off the pipe. virtual void Connect( mojo::PendingAssociatedRemote* sender, mojo::PendingAssociatedReceiver* receiver) = 0; // Enable incoming messages to start being read off the pipe and routed to // endpoints. Must not be called until the pending endpoints created by // Connect() are actually bound somewhere. virtual void StartReceiving() = 0; // Stop transmitting messages and start queueing them instead. virtual void Pause() = 0; // Stop queuing new messages and start transmitting them instead. virtual void Unpause() = 0; // Flush outgoing messages which were queued before Start(). virtual void Flush() = 0; virtual mojo::AssociatedGroup* GetAssociatedGroup() = 0; }; } // namespace IPC #endif // IPC_IPC_MOJO_BOOTSTRAP_H_