text: // 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.
  
  #include <stddef.h>
  #include <memory>
  
  #include "base/memory/ptr_util.h"
  #include "base/message_loop/message_loop.h"
  #include "base/message_loop/message_loop_current.h"
  #include "base/process/process_metrics.h"
  #include "base/run_loop.h"
  #include "base/strings/stringprintf.h"
  #include "base/synchronization/waitable_event.h"
  #include "base/test/perf_time_logger.h"
  #include "base/threading/thread.h"
  #include "build/build_config.h"
  #include "ipc/ipc_channel_mojo.h"
  #include "ipc/ipc_perftest_messages.h"
  #include "ipc/ipc_perftest_util.h"
  #include "ipc/ipc_sync_channel.h"
  #include "ipc/ipc_test.mojom.h"
  #include "ipc/ipc_test_base.h"
  #include "mojo/edk/embedder/embedder.h"
  #include "mojo/edk/embedder/platform_channel_pair.h"
  #include "mojo/edk/test/mojo_test_base.h"
  #include "mojo/edk/test/multiprocess_test_helper.h"
  #include "mojo/public/cpp/bindings/associated_binding_set.h"
  #include "mojo/public/cpp/bindings/binding.h"
  #include "mojo/public/cpp/bindings/binding_set.h"
  #include "mojo/public/cpp/system/message_pipe.h"
  
  namespace IPC {
  namespace {
  
  class PerformanceChannelListener : public Listener {
   public:
    explicit PerformanceChannelListener(const std::string& label)
        : label_(label),
          sender_(NULL),
          msg_count_(0),
          msg_size_(0),
          sync_(false),
          count_down_(0) {
      VLOG(1) << "Server listener up";
    }
  
    ~PerformanceChannelListener() override { VLOG(1) << "Server listener down"; }
  
    void Init(Sender* sender) {
      DCHECK(!sender_);
      sender_ = sender;
    }
  
    // Call this before running the message loop.
    void SetTestParams(int msg_count, size_t msg_size, bool sync) {
      DCHECK_EQ(0, count_down_);
      msg_count_ = msg_count;
      msg_size_ = msg_size;
      sync_ = sync;
      count_down_ = msg_count_;
      payload_ = std::string(msg_size_, 'a');
    }
  
    bool OnMessageReceived(const Message& message) override {
      CHECK(sender_);
  
      bool handled = true;
      IPC_BEGIN_MESSAGE_MAP(PerformanceChannelListener, message)
        IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
        IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
        IPC_MESSAGE_UNHANDLED(handled = false)
      IPC_END_MESSAGE_MAP()
      return handled;
    }
  
    void OnHello() {
      // Start timing on hello.
      DCHECK(!perf_logger_.get());
      std::string test_name =
          base::StringPrintf("IPC_%s_Perf_%dx_%u", label_.c_str(), msg_count_,
                             static_cast<unsigned>(msg_size_));
      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
      if (sync_) {
        for (; count_down_ > 0; --count_down_) {
          std::string response;
          sender_->Send(new TestMsg_SyncPing(payload_, &response));
          DCHECK_EQ(response, payload_);
        }
        perf_logger_.reset();
        base::RunLoop::QuitCurrentWhenIdleDeprecated();
      } else {
        SendPong();
      }
    }
  
    void OnPing(const std::string& payload) {
      // Include message deserialization in latency.
      DCHECK_EQ(payload_.size(), payload.size());
  
      CHECK(count_down_ > 0);
      count_down_--;
      if (count_down_ == 0) {
        perf_logger_.reset();  // Stop the perf timer now.
        base::RunLoop::QuitCurrentWhenIdleDeprecated();
        return;
      }
  
      SendPong();
    }
  
    void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
  
   private:
    std::string label_;
    Sender* sender_;
    int msg_count_;
    size_t msg_size_;
    bool sync_;
  
    int count_down_;
    std::string payload_;
    std::unique_ptr<base::PerfTimeLogger> perf_logger_;
  };
  
  class PingPongTestParams {
   public:
    PingPongTestParams(size_t size, int count)
        : message_size_(size), message_count_(count) {}
  
    size_t message_size() const { return message_size_; }
    int message_count() const { return message_count_; }
  
   private:
    size_t message_size_;
    int message_count_;
  };
  
  class InterfacePassingTestParams {
   public:
    InterfacePassingTestParams(size_t rounds, size_t num_interfaces)
        : rounds_(rounds), num_interfaces_(num_interfaces) {}
  
    size_t rounds() const { return rounds_; }
    size_t num_interfaces() const { return num_interfaces_; }
  
   private:
    size_t rounds_;
    size_t num_interfaces_;
  };
  
  #ifdef NDEBUG
  const int kMultiplier = 100;
  #else
    // Debug builds on Windows run these tests orders of magnitude more slowly.
  const int kMultiplier = 1;
  #endif
  
  std::vector<PingPongTestParams> GetDefaultTestParams() {
    // Test several sizes. We use 12^N for message size, and limit the message
    // count to keep the test duration reasonable.
    std::vector<PingPongTestParams> list;
    list.push_back(PingPongTestParams(12, 500 * kMultiplier));
    list.push_back(PingPongTestParams(144, 500 * kMultiplier));
    list.push_back(PingPongTestParams(1728, 500 * kMultiplier));
    list.push_back(PingPongTestParams(20736, 120 * kMultiplier));
    list.push_back(PingPongTestParams(248832, 10 * kMultiplier));
    return list;
  }
  
  std::vector<InterfacePassingTestParams> GetDefaultInterfacePassingTestParams() {
    std::vector<InterfacePassingTestParams> list;
    list.push_back({500 * kMultiplier, 0});
    list.push_back({500 * kMultiplier, 1});
    list.push_back({500 * kMultiplier, 2});
    list.push_back({500 * kMultiplier, 4});
    list.push_back({500 * kMultiplier, 8});
    return list;
  }
  
  class MojoChannelPerfTest : public IPCChannelMojoTestBase {
   public:
    MojoChannelPerfTest() = default;
    ~MojoChannelPerfTest() override = default;
  
    void RunTestChannelProxyPingPong() {
      Init("MojoPerfTestClient");
  
      // Set up IPC channel and start client.
      PerformanceChannelListener listener("ChannelProxy");
      auto channel_proxy = IPC::ChannelProxy::Create(
          TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
          GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get());
      listener.Init(channel_proxy.get());
  
      LockThreadAffinity thread_locker(kSharedCore);
      std::vector<PingPongTestParams> params = GetDefaultTestParams();
      for (size_t i = 0; i < params.size(); i++) {
        listener.SetTestParams(params[i].message_count(),
                               params[i].message_size(), false);
  
        // This initial message will kick-start the ping-pong of messages.
        channel_proxy->Send(new TestMsg_Hello);
  
        // Run message loop.
        base::RunLoop().Run();
      }
  
      // Send quit message.
      channel_proxy->Send(new TestMsg_Quit);
  
      EXPECT_TRUE(WaitForClientShutdown());
      channel_proxy.reset();
    }
  
    void RunTestChannelProxySyncPing() {
      Init("MojoPerfTestClient");
  
      // Set up IPC channel and start client.
      PerformanceChannelListener listener("ChannelProxy");
      base::WaitableEvent shutdown_event(
          base::WaitableEvent::ResetPolicy::MANUAL,
          base::WaitableEvent::InitialState::NOT_SIGNALED);
      auto channel_proxy = IPC::SyncChannel::Create(
          TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
          GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get(), false,
          &shutdown_event);
      listener.Init(channel_proxy.get());
  
      LockThreadAffinity thread_locker(kSharedCore);
      std::vector<PingPongTestParams> params = GetDefaultTestParams();
      for (size_t i = 0; i < params.size(); i++) {
        listener.SetTestParams(params[i].message_count(),
                               params[i].message_size(), true);
  
        // This initial message will kick-start the ping-pong of messages.
        channel_proxy->Send(new TestMsg_Hello);
  
        // Run message loop.
        base::RunLoop().Run();
      }
  
      // Send quit message.
      channel_proxy->Send(new TestMsg_Quit);
  
      EXPECT_TRUE(WaitForClientShutdown());
      channel_proxy.reset();
    }
  };
  
  TEST_F(MojoChannelPerfTest, ChannelProxyPingPong) {
    RunTestChannelProxyPingPong();
  
    base::RunLoop run_loop;
    run_loop.RunUntilIdle();
  }
  
  TEST_F(MojoChannelPerfTest, ChannelProxySyncPing) {
    RunTestChannelProxySyncPing();
  
    base::RunLoop run_loop;
    run_loop.RunUntilIdle();
  }
  
  MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
    MojoPerfTestClient client;
    int rv = mojo::edk::test::MultiprocessTestHelper::RunClientMain(
        base::Bind(&MojoPerfTestClient::Run, base::Unretained(&client)),
        true /* pass_pipe_ownership_to_main */);
  
    base::RunLoop run_loop;
    run_loop.RunUntilIdle();
  
    return rv;
  }
  
  class MojoInterfacePerfTest : public mojo::edk::test::MojoTestBase {
   public:
    MojoInterfacePerfTest() : message_count_(0), count_down_(0) {}
  
   protected:
    void RunPingPongServer(MojoHandle mp, const std::string& label) {
      label_ = label;
  
      mojo::MessagePipeHandle mp_handle(mp);
      mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
      ping_receiver_.Bind(IPC::mojom::ReflectorPtrInfo(std::move(scoped_mp), 0u));
  
      LockThreadAffinity thread_locker(kSharedCore);
      std::vector<PingPongTestParams> params = GetDefaultTestParams();
      for (size_t i = 0; i < params.size(); i++) {
        ping_receiver_->Ping("hello", base::Bind(&MojoInterfacePerfTest::OnPong,
                                                 base::Unretained(this)));
        message_count_ = count_down_ = params[i].message_count();
        payload_ = std::string(params[i].message_size(), 'a');
  
        base::RunLoop().Run();
      }
  
      ping_receiver_->Quit();
  
      ignore_result(ping_receiver_.PassInterface().PassHandle().release());
    }
  
    void OnPong(const std::string& value) {
      if (value == "hello") {
        DCHECK(!perf_logger_.get());
        std::string test_name =
            base::StringPrintf("IPC_%s_Perf_%dx_%zu", label_.c_str(),
                               message_count_, payload_.size());
        perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
      } else {
        DCHECK_EQ(payload_.size(), value.size());
  
        CHECK(count_down_ > 0);
        count_down_--;
        if (count_down_ == 0) {
          perf_logger_.reset();
          base::RunLoop::QuitCurrentWhenIdleDeprecated();
          return;
        }
      }
  
      if (sync_) {
        for (int i = 0; i < count_down_; ++i) {
          std::string response;
          ping_receiver_->SyncPing(payload_, &response);
          DCHECK_EQ(response, payload_);
        }
        perf_logger_.reset();
        base::RunLoop::QuitCurrentWhenIdleDeprecated();
      } else {
        ping_receiver_->Ping(payload_, base::Bind(&MojoInterfacePerfTest::OnPong,
                                                  base::Unretained(this)));
      }
    }
  
    static int RunPingPongClient(MojoHandle mp) {
      mojo::MessagePipeHandle mp_handle(mp);
      mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
  
      LockThreadAffinity thread_locker(kSharedCore);
      // In single process mode, this is running in a task and by default other
      // tasks (in particular, the binding) won't run. To keep the single process
      // and multi-process code paths the same, enable nestable tasks.
      base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
      ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
      run_loop.Run();
      return 0;
    }
  
    bool sync_ = false;
  
   private:
    int message_count_;
    int count_down_;
    std::string label_;
    std::string payload_;
    IPC::mojom::ReflectorPtr ping_receiver_;
    std::unique_ptr<base::PerfTimeLogger> perf_logger_;
  
    DISALLOW_COPY_AND_ASSIGN(MojoInterfacePerfTest);
  };
  
  class InterfacePassingTestDriverImpl : public mojom::InterfacePassingTestDriver,
                                         public mojom::PingReceiver {
   public:
    InterfacePassingTestDriverImpl(mojo::ScopedMessagePipeHandle handle,
                                   const base::Closure& quit_closure)
        : binding_(this,
                   mojom::InterfacePassingTestDriverRequest(std::move(handle))),
          quit_closure_(quit_closure) {}
    ~InterfacePassingTestDriverImpl() override {
      ignore_result(binding_.Unbind().PassMessagePipe().release());
    }
  
   private:
    // mojom::InterfacePassingTestDriver implementation:
    void Init(InitCallback callback) override { std::move(callback).Run(); }
  
    void GetPingReceiver(std::vector<mojom::PingReceiverRequest> requests,
                         GetPingReceiverCallback callback) override {
      for (auto& request : requests)
        ping_receiver_bindings_.AddBinding(this, std::move(request));
      ping_receiver_bindings_.CloseAllBindings();
      std::move(callback).Run();
    }
  
    void GetAssociatedPingReceiver(
        std::vector<mojom::PingReceiverAssociatedRequest> requests,
        GetAssociatedPingReceiverCallback callback) override {
      for (auto& request : requests)
        ping_receiver_associated_bindings_.AddBinding(this, std::move(request));
      ping_receiver_associated_bindings_.CloseAllBindings();
      std::move(callback).Run();
    }
  
    void Quit() override {
      if (quit_closure_)
        quit_closure_.Run();
    }
  
    // mojom::PingReceiver implementation:
    void Ping(PingCallback callback) override { std::move(callback).Run(); }
  
    mojo::BindingSet<mojom::PingReceiver> ping_receiver_bindings_;
    mojo::AssociatedBindingSet<mojom::PingReceiver>
        ping_receiver_associated_bindings_;
    mojo::Binding<mojom::InterfacePassingTestDriver> binding_;
  
    base::Closure quit_closure_;
  };
  
  class MojoInterfacePassingPerfTest : public mojo::edk::test::MojoTestBase {
   public:
    MojoInterfacePassingPerfTest() = default;
  
   protected:
    void RunInterfacePassingServer(MojoHandle mp,
                                   const std::string& label,
                                   bool associated) {
      label_ = label;
      associated_ = associated;
  
      mojo::MessagePipeHandle mp_handle(mp);
      mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
      driver_ptr_.Bind(
          mojom::InterfacePassingTestDriverPtrInfo(std::move(scoped_mp), 0u));
  
      auto params = GetDefaultInterfacePassingTestParams();
  
      LockThreadAffinity thread_locker(kSharedCore);
      for (size_t i = 0; i < params.size(); ++i) {
        driver_ptr_->Init(
            base::Bind(&MojoInterfacePassingPerfTest::OnInitCallback,
                       base::Unretained(this)));
        rounds_ = count_down_ = params[i].rounds();
        num_interfaces_ = params[i].num_interfaces();
  
        base::RunLoop run_loop;
        quit_closure_ = run_loop.QuitWhenIdleClosure();
        run_loop.Run();
      }
  
      driver_ptr_->Quit();
  
      ignore_result(driver_ptr_.PassInterface().PassHandle().release());
    }
  
    void OnInitCallback() {
      DCHECK(!perf_logger_.get());
      std::string test_name = base::StringPrintf(
          "IPC_%s_Perf_%zux_%zu", label_.c_str(), rounds_, num_interfaces_);
      perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
  
      DoNextRound();
    }
  
    void DoNextRound() {
      if (associated_) {
        std::vector<mojom::PingReceiverAssociatedPtr> associated_interfaces(
            num_interfaces_);
  
        std::vector<mojom::PingReceiverAssociatedRequest> requests(
            num_interfaces_);
        for (size_t i = 0; i < num_interfaces_; ++i) {
          requests[i] = mojo::MakeRequest(&associated_interfaces[i]);
          // Force the interface pointer to do full initialization.
          associated_interfaces[i].get();
        }
  
        driver_ptr_->GetAssociatedPingReceiver(
            std::move(requests),
            base::Bind(&MojoInterfacePassingPerfTest::OnGetReceiverCallback,
                       base::Unretained(this)));
      } else {
        std::vector<mojom::PingReceiverPtr> interfaces(num_interfaces_);
  
        std::vector<mojom::PingReceiverRequest> requests(num_interfaces_);
        for (size_t i = 0; i < num_interfaces_; ++i) {
          requests[i] = mojo::MakeRequest(&interfaces[i]);
          // Force the interface pointer to do full initialization.
          interfaces[i].get();
        }
  
        driver_ptr_->GetPingReceiver(
            std::move(requests),
            base::Bind(&MojoInterfacePassingPerfTest::OnGetReceiverCallback,
                       base::Unretained(this)));
      }
    }
  
    void OnGetReceiverCallback() {
      CHECK_GT(count_down_, 0u);
      count_down_--;
  
      if (count_down_ == 0) {
        perf_logger_.reset();
        quit_closure_.Run();
        return;
      }
  
      DoNextRound();
    }
  
    static int RunInterfacePassingClient(MojoHandle mp) {
      mojo::MessagePipeHandle mp_handle(mp);
      mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
  
      LockThreadAffinity thread_locker(kSharedCore);
      // In single process mode, this is running in a task and by default other
      // tasks (in particular, the binding) won't run. To keep the single process
      // and multi-process code paths the same, enable nestable tasks.
      base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
      InterfacePassingTestDriverImpl impl(std::move(scoped_mp),
                                          run_loop.QuitWhenIdleClosure());
      run_loop.Run();
      return 0;
    }
  
   private:
    size_t rounds_ = 0;
    size_t count_down_ = 0;
    size_t num_interfaces_ = 0;
    std::string label_;
    bool associated_ = false;
    std::unique_ptr<base::PerfTimeLogger> perf_logger_;
  
    mojom::InterfacePassingTestDriverPtr driver_ptr_;
  
    base::Closure quit_closure_;
  
    DISALLOW_COPY_AND_ASSIGN(MojoInterfacePassingPerfTest);
  };
  
  DEFINE_TEST_CLIENT_WITH_PIPE(InterfacePassingClient,
                               MojoInterfacePassingPerfTest,
                               h) {
    base::MessageLoop main_message_loop;
    return RunInterfacePassingClient(h);
  }
  
  enum class InProcessMessageMode {
    kSerialized,
    kUnserialized,
  };
  
  template <class TestBase>
  class InProcessPerfTest
      : public TestBase,
        public testing::WithParamInterface<InProcessMessageMode> {
   public:
    InProcessPerfTest() {
      switch (GetParam()) {
        case InProcessMessageMode::kSerialized:
          mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(
              mojo::Connector::OutgoingSerializationMode::kEager,
              mojo::Connector::IncomingSerializationMode::kDispatchAsIs);
          break;
        case InProcessMessageMode::kUnserialized:
          mojo::Connector::OverrideDefaultSerializationBehaviorForTesting(
              mojo::Connector::OutgoingSerializationMode::kLazy,
              mojo::Connector::IncomingSerializationMode::kDispatchAsIs);
          break;
      }
    }
  };
  
  using MojoInProcessInterfacePerfTest = InProcessPerfTest<MojoInterfacePerfTest>;
  using MojoInProcessInterfacePassingPerfTest =
      InProcessPerfTest<MojoInterfacePassingPerfTest>;
  
  DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoInterfacePerfTest, h) {
    base::MessageLoop main_message_loop;
    return RunPingPongClient(h);
  }
  
  // Similar to MojoChannelPerfTest above, but uses a Mojo interface instead of
  // raw IPC::Messages.
  TEST_F(MojoInterfacePerfTest, MultiprocessPingPong) {
    RunTestClient("PingPongClient", [&](MojoHandle h) {
      base::MessageLoop main_message_loop;
      RunPingPongServer(h, "Multiprocess");
    });
  }
  
  TEST_F(MojoInterfacePerfTest, MultiprocessSyncPing) {
    sync_ = true;
    RunTestClient("PingPongClient", [&](MojoHandle h) {
      base::MessageLoop main_message_loop;
      RunPingPongServer(h, "MultiprocessSync");
    });
  }
  
  TEST_F(MojoInterfacePassingPerfTest, MultiprocessInterfacePassing) {
    RunTestClient("InterfacePassingClient", [&](MojoHandle h) {
      base::MessageLoop main_message_loop;
      RunInterfacePassingServer(h, "InterfacePassing", false /* associated */);
    });
  }
  
  TEST_F(MojoInterfacePassingPerfTest, MultiprocessAssociatedInterfacePassing) {
    RunTestClient("InterfacePassingClient", [&](MojoHandle h) {
      base::MessageLoop main_message_loop;
      RunInterfacePassingServer(h, "AssociatedInterfacePassing",
                                true /* associated*/);
    });
  }
  
  // A single process version of the above test.
  TEST_P(MojoInProcessInterfacePerfTest, MultiThreadPingPong) {
    MojoHandle server_handle, client_handle;
    CreateMessagePipe(&server_handle, &client_handle);
  
    base::Thread client_thread("PingPongClient");
    client_thread.Start();
    client_thread.task_runner()->PostTask(
        FROM_HERE,
        base::Bind(base::IgnoreResult(&RunPingPongClient), client_handle));
  
    base::MessageLoop main_message_loop;
    RunPingPongServer(server_handle, "SingleProcess");
  }
  
  TEST_P(MojoInProcessInterfacePerfTest, SingleThreadPingPong) {
    MojoHandle server_handle, client_handle;
    CreateMessagePipe(&server_handle, &client_handle);
  
    base::MessageLoop main_message_loop;
    mojo::MessagePipeHandle mp_handle(client_handle);
    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
    LockThreadAffinity thread_locker(kSharedCore);
    ReflectorImpl impl(std::move(scoped_mp), base::Closure());
  
    RunPingPongServer(server_handle, "SingleProcess");
  }
  
  INSTANTIATE_TEST_CASE_P(,
                          MojoInProcessInterfacePerfTest,
                          testing::Values(InProcessMessageMode::kSerialized,
                                          InProcessMessageMode::kUnserialized));
  
  TEST_P(MojoInProcessInterfacePassingPerfTest, MultiThreadInterfacePassing) {
    MojoHandle server_handle, client_handle;
    CreateMessagePipe(&server_handle, &client_handle);
  
    base::Thread client_thread("InterfacePassingClient");
    client_thread.Start();
    client_thread.task_runner()->PostTask(
        FROM_HERE, base::Bind(base::IgnoreResult(&RunInterfacePassingClient),
                              client_handle));
  
    base::MessageLoop main_message_loop;
    RunInterfacePassingServer(server_handle, "SingleProcess",
                              false /* associated */);
  }
  
  TEST_P(MojoInProcessInterfacePassingPerfTest,
         MultiThreadAssociatedInterfacePassing) {
    MojoHandle server_handle, client_handle;
    CreateMessagePipe(&server_handle, &client_handle);
  
    base::Thread client_thread("InterfacePassingClient");
    client_thread.Start();
    client_thread.task_runner()->PostTask(
        FROM_HERE, base::Bind(base::IgnoreResult(&RunInterfacePassingClient),
                              client_handle));
  
    base::MessageLoop main_message_loop;
    RunInterfacePassingServer(server_handle, "SingleProcess",
                              true /* associated */);
  }
  
  TEST_P(MojoInProcessInterfacePassingPerfTest, SingleThreadInterfacePassing) {
    MojoHandle server_handle, client_handle;
    CreateMessagePipe(&server_handle, &client_handle);
  
    base::MessageLoop main_message_loop;
    mojo::MessagePipeHandle mp_handle(client_handle);
    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
    LockThreadAffinity thread_locker(kSharedCore);
    InterfacePassingTestDriverImpl impl(std::move(scoped_mp), base::Closure());
  
    RunInterfacePassingServer(server_handle, "SingleProcess",
                              false /* associated */);
  }
  
  TEST_P(MojoInProcessInterfacePassingPerfTest,
         SingleThreadAssociatedInterfacePassing) {
    MojoHandle server_handle, client_handle;
    CreateMessagePipe(&server_handle, &client_handle);
  
    base::MessageLoop main_message_loop;
    mojo::MessagePipeHandle mp_handle(client_handle);
    mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
    LockThreadAffinity thread_locker(kSharedCore);
    InterfacePassingTestDriverImpl impl(std::move(scoped_mp), base::Closure());
  
    RunInterfacePassingServer(server_handle, "SingleProcess",
                              true /* associated */);
  }
  
  INSTANTIATE_TEST_CASE_P(,
                          MojoInProcessInterfacePassingPerfTest,
                          testing::Values(InProcessMessageMode::kSerialized,
                                          InProcessMessageMode::kUnserialized));
  
  class CallbackPerfTest : public testing::Test {
   public:
    CallbackPerfTest()
        : client_thread_("PingPongClient"), message_count_(0), count_down_(0) {}
  
   protected:
    void RunMultiThreadPingPongServer() {
      client_thread_.Start();
  
      LockThreadAffinity thread_locker(kSharedCore);
      std::vector<PingPongTestParams> params = GetDefaultTestParams();
      for (size_t i = 0; i < params.size(); i++) {
        std::string hello("hello");
        client_thread_.task_runner()->PostTask(
            FROM_HERE,
            base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), hello));
        message_count_ = count_down_ = params[i].message_count();
        payload_ = std::string(params[i].message_size(), 'a');
  
        base::RunLoop().Run();
      }
    }
  
    void Ping(const std::string& value) {
      main_message_loop_.task_runner()->PostTask(
          FROM_HERE,
          base::Bind(&CallbackPerfTest::OnPong, base::Unretained(this), value));
    }
  
    void OnPong(const std::string& value) {
      if (value == "hello") {
        DCHECK(!perf_logger_.get());
        std::string test_name =
            base::StringPrintf("Callback_MultiProcess_Perf_%dx_%zu",
                               message_count_, payload_.size());
        perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
      } else {
        DCHECK_EQ(payload_.size(), value.size());
  
        CHECK(count_down_ > 0);
        count_down_--;
        if (count_down_ == 0) {
          perf_logger_.reset();
          base::RunLoop::QuitCurrentWhenIdleDeprecated();
          return;
        }
      }
  
      client_thread_.task_runner()->PostTask(
          FROM_HERE,
          base::Bind(&CallbackPerfTest::Ping, base::Unretained(this), payload_));
    }
  
    void RunSingleThreadNoPostTaskPingPongServer() {
      LockThreadAffinity thread_locker(kSharedCore);
      std::vector<PingPongTestParams> params = GetDefaultTestParams();
      base::Callback<void(const std::string&, int,
                          const base::Callback<void(const std::string&, int)>&)>
          ping = base::Bind(&CallbackPerfTest::SingleThreadPingNoPostTask,
                            base::Unretained(this));
      for (size_t i = 0; i < params.size(); i++) {
        payload_ = std::string(params[i].message_size(), 'a');
        std::string test_name =
            base::StringPrintf("Callback_SingleThreadNoPostTask_Perf_%dx_%zu",
                               params[i].message_count(), payload_.size());
        perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
        for (int j = 0; j < params[i].message_count(); ++j) {
          ping.Run(payload_, j,
                   base::Bind(&CallbackPerfTest::SingleThreadPongNoPostTask,
                              base::Unretained(this)));
        }
        perf_logger_.reset();
      }
    }
  
    void SingleThreadPingNoPostTask(
        const std::string& value,
        int i,
        const base::Callback<void(const std::string&, int)>& pong) {
      pong.Run(value, i);
    }
  
    void SingleThreadPongNoPostTask(const std::string& value, int i) {}
  
    void RunSingleThreadPostTaskPingPongServer() {
      LockThreadAffinity thread_locker(kSharedCore);
      std::vector<PingPongTestParams> params = GetDefaultTestParams();
      for (size_t i = 0; i < params.size(); i++) {
        std::string hello("hello");
        base::MessageLoopCurrent::Get()->task_runner()->PostTask(
            FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPingPostTask,
                                  base::Unretained(this), hello));
        message_count_ = count_down_ = params[i].message_count();
        payload_ = std::string(params[i].message_size(), 'a');
  
        base::RunLoop().Run();
      }
    }
  
    void SingleThreadPingPostTask(const std::string& value) {
      base::MessageLoopCurrent::Get()->task_runner()->PostTask(
          FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPongPostTask,
                                base::Unretained(this), value));
    }
  
    void SingleThreadPongPostTask(const std::string& value) {
      if (value == "hello") {
        DCHECK(!perf_logger_.get());
        std::string test_name =
            base::StringPrintf("Callback_SingleThreadPostTask_Perf_%dx_%zu",
                               message_count_, payload_.size());
        perf_logger_.reset(new base::PerfTimeLogger(test_name.c_str()));
      } else {
        DCHECK_EQ(payload_.size(), value.size());
  
        CHECK(count_down_ > 0);
        count_down_--;
        if (count_down_ == 0) {
          perf_logger_.reset();
          base::RunLoop::QuitCurrentWhenIdleDeprecated();
          return;
        }
      }
  
      base::MessageLoopCurrent::Get()->task_runner()->PostTask(
          FROM_HERE, base::Bind(&CallbackPerfTest::SingleThreadPingPostTask,
                                base::Unretained(this), payload_));
    }
  
   private:
    base::Thread client_thread_;
    base::MessageLoop main_message_loop_;
    int message_count_;
    int count_down_;
    std::string payload_;
    std::unique_ptr<base::PerfTimeLogger> perf_logger_;
  
    DISALLOW_COPY_AND_ASSIGN(CallbackPerfTest);
  };
  
  // Sends the same data as above using PostTask to a different thread instead of
  // IPCs for comparison.
  TEST_F(CallbackPerfTest, MultiThreadPingPong) {
    RunMultiThreadPingPongServer();
  }
  
  // Sends the same data as above using PostTask to the same thread.
  TEST_F(CallbackPerfTest, SingleThreadPostTaskPingPong) {
    RunSingleThreadPostTaskPingPongServer();
  }
  
  // Sends the same data as above without using PostTask to the same thread.
  TEST_F(CallbackPerfTest, SingleThreadNoPostTaskPingPong) {
    RunSingleThreadNoPostTaskPingPongServer();
  }
  
  }  // namespace
  }  // namespace IPC
  