1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 #include <utility>
7 
8 #include "base/bind.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/threading/thread_task_runner_handle.h"
12 #include "base/time/time.h"
13 #include "mojo/public/cpp/bindings/binding.h"
14 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
15 #include "mojo/public/cpp/bindings/lib/multiplex_router.h"
16 #include "mojo/public/cpp/bindings/message.h"
17 #include "mojo/public/cpp/test_support/test_support.h"
18 #include "mojo/public/cpp/test_support/test_utils.h"
19 #include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 
22 namespace mojo {
23 namespace {
24 
25 const double kMojoTicksPerSecond = 1000000.0;
26 
MojoTicksToSeconds(MojoTimeTicks ticks)27 double MojoTicksToSeconds(MojoTimeTicks ticks) {
28   return ticks / kMojoTicksPerSecond;
29 }
30 
31 class PingServiceImpl : public test::PingService {
32  public:
PingServiceImpl()33   PingServiceImpl() {}
~PingServiceImpl()34   ~PingServiceImpl() override {}
35 
36   // |PingService| methods:
37   void Ping(const PingCallback& callback) override;
38 
39  private:
40   DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
41 };
42 
Ping(const PingCallback & callback)43 void PingServiceImpl::Ping(const PingCallback& callback) {
44   callback.Run();
45 }
46 
47 class PingPongTest {
48  public:
49   explicit PingPongTest(test::PingServicePtr service);
50 
51   void Run(unsigned int iterations);
52 
53  private:
54   void OnPingDone();
55 
56   test::PingServicePtr service_;
57   unsigned int iterations_to_run_;
58   unsigned int current_iterations_;
59 
60   base::Closure quit_closure_;
61 
62   DISALLOW_COPY_AND_ASSIGN(PingPongTest);
63 };
64 
PingPongTest(test::PingServicePtr service)65 PingPongTest::PingPongTest(test::PingServicePtr service)
66     : service_(std::move(service)) {}
67 
Run(unsigned int iterations)68 void PingPongTest::Run(unsigned int iterations) {
69   iterations_to_run_ = iterations;
70   current_iterations_ = 0;
71 
72   base::RunLoop run_loop;
73   quit_closure_ = run_loop.QuitClosure();
74   service_->Ping(base::Bind(&PingPongTest::OnPingDone, base::Unretained(this)));
75   run_loop.Run();
76 }
77 
OnPingDone()78 void PingPongTest::OnPingDone() {
79   current_iterations_++;
80   if (current_iterations_ >= iterations_to_run_) {
81     quit_closure_.Run();
82     return;
83   }
84 
85   service_->Ping(base::Bind(&PingPongTest::OnPingDone, base::Unretained(this)));
86 }
87 
88 struct BoundPingService {
BoundPingServicemojo::__anon25a194a60111::BoundPingService89   BoundPingService() : binding(&impl) { binding.Bind(MakeRequest(&service)); }
90 
91   PingServiceImpl impl;
92   test::PingServicePtr service;
93   Binding<test::PingService> binding;
94 };
95 
96 class MojoBindingsPerftest : public testing::Test {
97  public:
MojoBindingsPerftest()98   MojoBindingsPerftest() {}
99 
100  protected:
101   base::MessageLoop loop_;
102 };
103 
TEST_F(MojoBindingsPerftest,InProcessPingPong)104 TEST_F(MojoBindingsPerftest, InProcessPingPong) {
105   test::PingServicePtr service;
106   PingServiceImpl impl;
107   Binding<test::PingService> binding(&impl, MakeRequest(&service));
108   PingPongTest test(std::move(service));
109 
110   {
111     const unsigned int kIterations = 100000;
112     const MojoTimeTicks start_time = MojoGetTimeTicksNow();
113     test.Run(kIterations);
114     const MojoTimeTicks end_time = MojoGetTimeTicksNow();
115     test::LogPerfResult(
116         "InProcessPingPong", "0_Inactive",
117         kIterations / MojoTicksToSeconds(end_time - start_time),
118         "pings/second");
119   }
120 
121   {
122     const size_t kNumInactiveServices = 1000;
123     BoundPingService* inactive_services =
124         new BoundPingService[kNumInactiveServices];
125 
126     const unsigned int kIterations = 10000;
127     const MojoTimeTicks start_time = MojoGetTimeTicksNow();
128     test.Run(kIterations);
129     const MojoTimeTicks end_time = MojoGetTimeTicksNow();
130     test::LogPerfResult(
131         "InProcessPingPong", "1000_Inactive",
132         kIterations / MojoTicksToSeconds(end_time - start_time),
133         "pings/second");
134 
135     delete[] inactive_services;
136   }
137 }
138 
139 class PingPongPaddle : public MessageReceiverWithResponderStatus {
140  public:
PingPongPaddle(MessageReceiver * sender)141   PingPongPaddle(MessageReceiver* sender) : sender_(sender) {}
142 
set_sender(MessageReceiver * sender)143   void set_sender(MessageReceiver* sender) { sender_ = sender; }
144 
Accept(Message * message)145   bool Accept(Message* message) override {
146     uint32_t count = message->header()->name;
147     if (!quit_closure_.is_null()) {
148       count++;
149       if (count >= expected_count_) {
150         end_time_ = base::TimeTicks::Now();
151         quit_closure_.Run();
152         return true;
153       }
154     }
155 
156     Message reply(count, 0, 0, 0, nullptr);
157     bool result = sender_->Accept(&reply);
158     DCHECK(result);
159     return true;
160   }
161 
AcceptWithResponder(Message * message,std::unique_ptr<MessageReceiverWithStatus> responder)162   bool AcceptWithResponder(
163       Message* message,
164       std::unique_ptr<MessageReceiverWithStatus> responder) override {
165     NOTREACHED();
166     return true;
167   }
168 
Serve(uint32_t expected_count)169   base::TimeDelta Serve(uint32_t expected_count) {
170     base::RunLoop run_loop;
171 
172     expected_count_ = expected_count;
173     quit_closure_ = run_loop.QuitClosure();
174 
175     start_time_ = base::TimeTicks::Now();
176     Message message(0, 0, 0, 0, nullptr);
177     bool result = sender_->Accept(&message);
178     DCHECK(result);
179 
180     run_loop.Run();
181 
182     return end_time_ - start_time_;
183   }
184 
185  private:
186   base::TimeTicks start_time_;
187   base::TimeTicks end_time_;
188   uint32_t expected_count_ = 0;
189   MessageReceiver* sender_;
190   base::Closure quit_closure_;
191 };
192 
TEST_F(MojoBindingsPerftest,MultiplexRouterPingPong)193 TEST_F(MojoBindingsPerftest, MultiplexRouterPingPong) {
194   MessagePipe pipe;
195   scoped_refptr<internal::MultiplexRouter> router0(
196       new internal::MultiplexRouter(std::move(pipe.handle0),
197                                     internal::MultiplexRouter::SINGLE_INTERFACE,
198                                     true, base::ThreadTaskRunnerHandle::Get()));
199   scoped_refptr<internal::MultiplexRouter> router1(
200       new internal::MultiplexRouter(
201           std::move(pipe.handle1), internal::MultiplexRouter::SINGLE_INTERFACE,
202           false, base::ThreadTaskRunnerHandle::Get()));
203 
204   PingPongPaddle paddle0(nullptr);
205   PingPongPaddle paddle1(nullptr);
206 
207   InterfaceEndpointClient client0(
208       router0->CreateLocalEndpointHandle(kMasterInterfaceId), &paddle0, nullptr,
209       false, base::ThreadTaskRunnerHandle::Get(), 0u);
210   InterfaceEndpointClient client1(
211       router1->CreateLocalEndpointHandle(kMasterInterfaceId), &paddle1, nullptr,
212       false, base::ThreadTaskRunnerHandle::Get(), 0u);
213 
214   paddle0.set_sender(&client0);
215   paddle1.set_sender(&client1);
216 
217   static const uint32_t kWarmUpIterations = 1000;
218   static const uint32_t kTestIterations = 1000000;
219 
220   paddle0.Serve(kWarmUpIterations);
221 
222   base::TimeDelta duration = paddle0.Serve(kTestIterations);
223 
224   test::LogPerfResult("MultiplexRouterPingPong", nullptr,
225                       kTestIterations / duration.InSecondsF(), "pings/second");
226 }
227 
228 class CounterReceiver : public MessageReceiverWithResponderStatus {
229  public:
Accept(Message * message)230   bool Accept(Message* message) override {
231     counter_++;
232     return true;
233   }
234 
AcceptWithResponder(Message * message,std::unique_ptr<MessageReceiverWithStatus> responder)235   bool AcceptWithResponder(
236       Message* message,
237       std::unique_ptr<MessageReceiverWithStatus> responder) override {
238     NOTREACHED();
239     return true;
240   }
241 
counter() const242   uint32_t counter() const { return counter_; }
243 
Reset()244   void Reset() { counter_ = 0; }
245 
246  private:
247   uint32_t counter_ = 0;
248 };
249 
TEST_F(MojoBindingsPerftest,MultiplexRouterDispatchCost)250 TEST_F(MojoBindingsPerftest, MultiplexRouterDispatchCost) {
251   MessagePipe pipe;
252   scoped_refptr<internal::MultiplexRouter> router(new internal::MultiplexRouter(
253       std::move(pipe.handle0), internal::MultiplexRouter::SINGLE_INTERFACE,
254       true, base::ThreadTaskRunnerHandle::Get()));
255   CounterReceiver receiver;
256   InterfaceEndpointClient client(
257       router->CreateLocalEndpointHandle(kMasterInterfaceId), &receiver, nullptr,
258       false, base::ThreadTaskRunnerHandle::Get(), 0u);
259 
260   static const uint32_t kIterations[] = {1000, 3000000};
261 
262   for (size_t i = 0; i < 2; ++i) {
263     receiver.Reset();
264     base::TimeTicks start_time = base::TimeTicks::Now();
265     for (size_t j = 0; j < kIterations[i]; ++j) {
266       Message message(0, 0, 8, 0, nullptr);
267       bool result = router->SimulateReceivingMessageForTesting(&message);
268       DCHECK(result);
269     }
270 
271     base::TimeTicks end_time = base::TimeTicks::Now();
272     base::TimeDelta duration = end_time - start_time;
273     CHECK_EQ(kIterations[i], receiver.counter());
274 
275     if (i == 1) {
276       test::LogPerfResult("MultiplexRouterDispatchCost", nullptr,
277                           kIterations[i] / duration.InSecondsF(),
278                           "times/second");
279     }
280   }
281 }
282 
283 }  // namespace
284 }  // namespace mojo
285