// 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 <stdint.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "base/test/perf_time_logger.h"
#include "base/threading/thread.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/handle_signals_state.h"
#include "mojo/edk/system/test_utils.h"
#include "mojo/edk/test/mojo_test_base.h"
#include "mojo/edk/test/test_utils.h"
#include "mojo/public/c/system/functions.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace edk {
namespace {
class MessagePipePerfTest : public test::MojoTestBase {
public:
MessagePipePerfTest() : message_count_(0), message_size_(0) {}
void SetUpMeasurement(int message_count, size_t message_size) {
message_count_ = message_count;
message_size_ = message_size;
payload_ = std::string(message_size, '*');
read_buffer_.resize(message_size * 2);
}
protected:
void WriteWaitThenRead(MojoHandle mp) {
CHECK_EQ(MojoWriteMessage(mp, payload_.data(),
static_cast<uint32_t>(payload_.size()), nullptr,
0, MOJO_WRITE_MESSAGE_FLAG_NONE),
MOJO_RESULT_OK);
HandleSignalsState hss;
CHECK_EQ(MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
&hss),
MOJO_RESULT_OK);
uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer_.size());
CHECK_EQ(MojoReadMessage(mp, &read_buffer_[0], &read_buffer_size, nullptr,
nullptr, MOJO_READ_MESSAGE_FLAG_NONE),
MOJO_RESULT_OK);
CHECK_EQ(read_buffer_size, static_cast<uint32_t>(payload_.size()));
}
void SendQuitMessage(MojoHandle mp) {
CHECK_EQ(MojoWriteMessage(mp, "", 0, nullptr, 0,
MOJO_WRITE_MESSAGE_FLAG_NONE),
MOJO_RESULT_OK);
}
void Measure(MojoHandle mp) {
// Have one ping-pong to ensure channel being established.
WriteWaitThenRead(mp);
std::string test_name =
base::StringPrintf("IPC_Perf_%dx_%u", message_count_,
static_cast<unsigned>(message_size_));
base::PerfTimeLogger logger(test_name.c_str());
for (int i = 0; i < message_count_; ++i)
WriteWaitThenRead(mp);
logger.Done();
}
protected:
void RunPingPongServer(MojoHandle mp) {
// This values are set to align with one at ipc_pertests.cc for comparison.
const size_t kMsgSize[5] = {12, 144, 1728, 20736, 248832};
const int kMessageCount[5] = {50000, 50000, 50000, 12000, 1000};
for (size_t i = 0; i < 5; i++) {
SetUpMeasurement(kMessageCount[i], kMsgSize[i]);
Measure(mp);
}
SendQuitMessage(mp);
}
static int RunPingPongClient(MojoHandle mp) {
std::string buffer(1000000, '\0');
int rv = 0;
while (true) {
// Wait for our end of the message pipe to be readable.
HandleSignalsState hss;
MojoResult result =
MojoWait(mp, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss);
if (result != MOJO_RESULT_OK) {
rv = result;
break;
}
uint32_t read_size = static_cast<uint32_t>(buffer.size());
CHECK_EQ(MojoReadMessage(mp, &buffer[0],
&read_size, nullptr,
0, MOJO_READ_MESSAGE_FLAG_NONE),
MOJO_RESULT_OK);
// Empty message indicates quit.
if (read_size == 0)
break;
CHECK_EQ(MojoWriteMessage(mp, &buffer[0],
read_size,
nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE),
MOJO_RESULT_OK);
}
return rv;
}
private:
int message_count_;
size_t message_size_;
std::string payload_;
std::string read_buffer_;
std::unique_ptr<base::PerfTimeLogger> perf_logger_;
DISALLOW_COPY_AND_ASSIGN(MessagePipePerfTest);
};
TEST_F(MessagePipePerfTest, PingPong) {
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));
RunPingPongServer(server_handle);
}
// For each message received, sends a reply message with the same contents
// repeated twice, until the other end is closed or it receives "quitquitquit"
// (which it doesn't reply to). It'll return the number of messages received,
// not including any "quitquitquit" message, modulo 100.
DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MessagePipePerfTest, h) {
return RunPingPongClient(h);
}
// Repeatedly sends messages as previous one got replied by the child.
// Waits for the child to close its end before quitting once specified
// number of messages has been sent.
TEST_F(MessagePipePerfTest, MultiprocessPingPong) {
RUN_CHILD_ON_PIPE(PingPongClient, h)
RunPingPongServer(h);
END_CHILD()
}
} // namespace
} // namespace edk
} // namespace mojo