Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc
new file mode 100644
index 0000000..7c7b400
--- /dev/null
+++ b/base/message_loop/message_pump_perftest.cc
@@ -0,0 +1,294 @@
+// 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 "base/bind.h"
+#include "base/format_macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/java_handler_thread.h"
+#endif
+
+namespace base {
+namespace {
+
+class ScheduleWorkTest : public testing::Test {
+ public:
+  ScheduleWorkTest() : counter_(0) {}
+
+  void Increment(uint64_t amount) { counter_ += amount; }
+
+  void Schedule(int index) {
+    base::TimeTicks start = base::TimeTicks::HighResNow();
+    base::TimeTicks thread_start;
+    if (TimeTicks::IsThreadNowSupported())
+      thread_start = base::TimeTicks::ThreadNow();
+    base::TimeDelta minimum = base::TimeDelta::Max();
+    base::TimeDelta maximum = base::TimeDelta();
+    base::TimeTicks now, lastnow = start;
+    uint64_t schedule_calls = 0u;
+    do {
+      for (size_t i = 0; i < kBatchSize; ++i) {
+        target_message_loop()->ScheduleWork(true);
+        schedule_calls++;
+      }
+      now = base::TimeTicks::HighResNow();
+      base::TimeDelta laptime = now - lastnow;
+      lastnow = now;
+      minimum = std::min(minimum, laptime);
+      maximum = std::max(maximum, laptime);
+    } while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec));
+
+    scheduling_times_[index] = now - start;
+    if (TimeTicks::IsThreadNowSupported())
+      scheduling_thread_times_[index] =
+          base::TimeTicks::ThreadNow() - thread_start;
+    min_batch_times_[index] = minimum;
+    max_batch_times_[index] = maximum;
+    target_message_loop()->PostTask(FROM_HERE,
+                                    base::Bind(&ScheduleWorkTest::Increment,
+                                               base::Unretained(this),
+                                               schedule_calls));
+  }
+
+  void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) {
+#if defined(OS_ANDROID)
+    if (target_type == MessageLoop::TYPE_JAVA) {
+      java_thread_.reset(new android::JavaHandlerThread("target"));
+      java_thread_->Start();
+    } else
+#endif
+    {
+      target_.reset(new Thread("target"));
+      target_->StartWithOptions(Thread::Options(target_type, 0u));
+    }
+
+    ScopedVector<Thread> scheduling_threads;
+    scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+    scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+    min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+    max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]);
+
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      scheduling_threads.push_back(new Thread("posting thread"));
+      scheduling_threads[i]->Start();
+    }
+
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      scheduling_threads[i]->message_loop()->PostTask(
+          FROM_HERE,
+          base::Bind(&ScheduleWorkTest::Schedule, base::Unretained(this), i));
+    }
+
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      scheduling_threads[i]->Stop();
+    }
+#if defined(OS_ANDROID)
+    if (target_type == MessageLoop::TYPE_JAVA) {
+      java_thread_->Stop();
+      java_thread_.reset();
+    } else
+#endif
+    {
+      target_->Stop();
+      target_.reset();
+    }
+    base::TimeDelta total_time;
+    base::TimeDelta total_thread_time;
+    base::TimeDelta min_batch_time = base::TimeDelta::Max();
+    base::TimeDelta max_batch_time = base::TimeDelta();
+    for (int i = 0; i < num_scheduling_threads; ++i) {
+      total_time += scheduling_times_[i];
+      total_thread_time += scheduling_thread_times_[i];
+      min_batch_time = std::min(min_batch_time, min_batch_times_[i]);
+      max_batch_time = std::max(max_batch_time, max_batch_times_[i]);
+    }
+    std::string trace = StringPrintf(
+        "%d_threads_scheduling_to_%s_pump",
+        num_scheduling_threads,
+        target_type == MessageLoop::TYPE_IO
+            ? "io"
+            : (target_type == MessageLoop::TYPE_UI ? "ui" : "default"));
+    perf_test::PrintResult(
+        "task",
+        "",
+        trace,
+        total_time.InMicroseconds() / static_cast<double>(counter_),
+        "us/task",
+        true);
+    perf_test::PrintResult(
+        "task",
+        "_min_batch_time",
+        trace,
+        min_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
+        "us/task",
+        false);
+    perf_test::PrintResult(
+        "task",
+        "_max_batch_time",
+        trace,
+        max_batch_time.InMicroseconds() / static_cast<double>(kBatchSize),
+        "us/task",
+        false);
+    if (TimeTicks::IsThreadNowSupported()) {
+      perf_test::PrintResult(
+          "task",
+          "_thread_time",
+          trace,
+          total_thread_time.InMicroseconds() / static_cast<double>(counter_),
+          "us/task",
+          true);
+    }
+  }
+
+  MessageLoop* target_message_loop() {
+#if defined(OS_ANDROID)
+    if (java_thread_)
+      return java_thread_->message_loop();
+#endif
+    return target_->message_loop();
+  }
+
+ private:
+  scoped_ptr<Thread> target_;
+#if defined(OS_ANDROID)
+  scoped_ptr<android::JavaHandlerThread> java_thread_;
+#endif
+  scoped_ptr<base::TimeDelta[]> scheduling_times_;
+  scoped_ptr<base::TimeDelta[]> scheduling_thread_times_;
+  scoped_ptr<base::TimeDelta[]> min_batch_times_;
+  scoped_ptr<base::TimeDelta[]> max_batch_times_;
+  uint64_t counter_;
+
+  static const size_t kTargetTimeSec = 5;
+  static const size_t kBatchSize = 1000;
+};
+
+TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_IO, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_IO, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_IO, 4);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_UI, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_UI, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_UI, 4);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_DEFAULT, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_DEFAULT, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_DEFAULT, 4);
+}
+
+#if defined(OS_ANDROID)
+TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) {
+  ScheduleWork(MessageLoop::TYPE_JAVA, 1);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) {
+  ScheduleWork(MessageLoop::TYPE_JAVA, 2);
+}
+
+TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) {
+  ScheduleWork(MessageLoop::TYPE_JAVA, 4);
+}
+#endif
+
+static void DoNothing() {
+}
+
+class FakeMessagePump : public MessagePump {
+ public:
+  FakeMessagePump() {}
+  virtual ~FakeMessagePump() {}
+
+  virtual void Run(Delegate* delegate) OVERRIDE {}
+
+  virtual void Quit() OVERRIDE {}
+  virtual void ScheduleWork() OVERRIDE {}
+  virtual void ScheduleDelayedWork(
+      const TimeTicks& delayed_work_time) OVERRIDE {}
+};
+
+class PostTaskTest : public testing::Test {
+ public:
+  void Run(int batch_size, int tasks_per_reload) {
+    base::TimeTicks start = base::TimeTicks::HighResNow();
+    base::TimeTicks now;
+    MessageLoop loop(scoped_ptr<MessagePump>(new FakeMessagePump));
+    scoped_refptr<internal::IncomingTaskQueue> queue(
+        new internal::IncomingTaskQueue(&loop));
+    uint32_t num_posted = 0;
+    do {
+      for (int i = 0; i < batch_size; ++i) {
+        for (int j = 0; j < tasks_per_reload; ++j) {
+          queue->AddToIncomingQueue(
+              FROM_HERE, base::Bind(&DoNothing), base::TimeDelta(), false);
+          num_posted++;
+        }
+        TaskQueue loop_local_queue;
+        queue->ReloadWorkQueue(&loop_local_queue);
+        while (!loop_local_queue.empty()) {
+          PendingTask t = loop_local_queue.front();
+          loop_local_queue.pop();
+          loop.RunTask(t);
+        }
+      }
+
+      now = base::TimeTicks::HighResNow();
+    } while (now - start < base::TimeDelta::FromSeconds(5));
+    std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload);
+    perf_test::PrintResult(
+        "task",
+        "",
+        trace,
+        (now - start).InMicroseconds() / static_cast<double>(num_posted),
+        "us/task",
+        true);
+  }
+};
+
+TEST_F(PostTaskTest, OneTaskPerReload) {
+  Run(10000, 1);
+}
+
+TEST_F(PostTaskTest, TenTasksPerReload) {
+  Run(10000, 10);
+}
+
+TEST_F(PostTaskTest, OneHundredTasksPerReload) {
+  Run(1000, 100);
+}
+
+}  // namespace
+}  // namespace base