// 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 <stdint.h>

#include <memory>

#include "base/component_export.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/task/single_thread_task_runner_forward.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<MojoBootstrap> Create(
      mojo::ScopedMessagePipeHandle handle,
      Channel::Mode mode,
      const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
      const scoped_refptr<mojo::internal::MessageQuotaChecker>& 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<mojom::Channel>* sender,
      mojo::PendingAssociatedReceiver<mojom::Channel>* 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_
