summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvmpstr@chromium.org <vmpstr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-29 01:47:53 +0000
committervmpstr@chromium.org <vmpstr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-29 01:47:53 +0000
commitafb84fc79640129ef90df715f24dd4d0e03381cd (patch)
tree695906cae568cabbecf297a2384aafbaa9615638
parent98b5897d51510e141d92d044eee69372c41b5ebe (diff)
downloadchromium_src-afb84fc79640129ef90df715f24dd4d0e03381cd.zip
chromium_src-afb84fc79640129ef90df715f24dd4d0e03381cd.tar.gz
chromium_src-afb84fc79640129ef90df715f24dd4d0e03381cd.tar.bz2
cc: Add UniqueNotifier to cc/base.
This patch adds a unique notifier, a class that supports scheduling a callback. It also supports calling additional schedules which effectively cancel a previous callback and reschedule a new one. Both version with delayed tasks and immediate tasks are supported. R=reveman BUG=none Review URL: https://codereview.chromium.org/296043005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273433 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--cc/base/delayed_unique_notifier.cc74
-rw-r--r--cc/base/delayed_unique_notifier.h58
-rw-r--r--cc/base/delayed_unique_notifier_unittest.cc257
-rw-r--r--cc/base/unique_notifier.cc42
-rw-r--r--cc/base/unique_notifier.h44
-rw-r--r--cc/base/unique_notifier_unittest.cc62
-rw-r--r--cc/cc.gyp4
-rw-r--r--cc/cc_tests.gyp2
8 files changed, 543 insertions, 0 deletions
diff --git a/cc/base/delayed_unique_notifier.cc b/cc/base/delayed_unique_notifier.cc
new file mode 100644
index 0000000..8086ba6
--- /dev/null
+++ b/cc/base/delayed_unique_notifier.cc
@@ -0,0 +1,74 @@
+// 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 "cc/base/delayed_unique_notifier.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+
+namespace cc {
+
+DelayedUniqueNotifier::DelayedUniqueNotifier(
+ base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure,
+ const base::TimeDelta& delay)
+ : task_runner_(task_runner),
+ closure_(closure),
+ delay_(delay),
+ notification_pending_(false),
+ weak_ptr_factory_(this) {
+}
+
+DelayedUniqueNotifier::~DelayedUniqueNotifier() {
+}
+
+void DelayedUniqueNotifier::Schedule() {
+ if (notification_pending_) {
+ next_notification_time_ = Now() + delay_;
+ return;
+ }
+
+ next_notification_time_ = Now() + delay_;
+ task_runner_->PostDelayedTask(FROM_HERE,
+ base::Bind(&DelayedUniqueNotifier::NotifyIfTime,
+ weak_ptr_factory_.GetWeakPtr()),
+ delay_);
+ notification_pending_ = true;
+}
+
+void DelayedUniqueNotifier::Cancel() {
+ next_notification_time_ = base::TimeTicks();
+}
+
+base::TimeTicks DelayedUniqueNotifier::Now() const {
+ return base::TimeTicks::Now();
+}
+
+void DelayedUniqueNotifier::NotifyIfTime() {
+ // If next notifiaction time is not valid, then this schedule was canceled.
+ if (next_notification_time_.is_null()) {
+ notification_pending_ = false;
+ return;
+ }
+
+ // If the notification was rescheduled or arrived too early for any other
+ // reason, then post another task instead of running the callback.
+ base::TimeTicks now = Now();
+ if (next_notification_time_ > now) {
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DelayedUniqueNotifier::NotifyIfTime,
+ weak_ptr_factory_.GetWeakPtr()),
+ next_notification_time_ - now);
+ return;
+ }
+
+ // Note the order here is important since closure might schedule another run.
+ notification_pending_ = false;
+ closure_.Run();
+}
+
+} // namespace cc
diff --git a/cc/base/delayed_unique_notifier.h b/cc/base/delayed_unique_notifier.h
new file mode 100644
index 0000000..2ae6ae0
--- /dev/null
+++ b/cc/base/delayed_unique_notifier.h
@@ -0,0 +1,58 @@
+// 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 CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
+#define CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
+
+namespace cc {
+
+class CC_EXPORT DelayedUniqueNotifier {
+ public:
+ // Configure this notifier to issue the |closure| notification in |delay| time
+ // from Schedule() call.
+ DelayedUniqueNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure,
+ const base::TimeDelta& delay);
+
+ // Destroying the notifier will ensure that no further notifications will
+ // happen from this class.
+ virtual ~DelayedUniqueNotifier();
+
+ // Schedule a notification to be run. If another notification is already
+ // pending, then it will happen in (at least) given delay from now. That is,
+ // if delay is 16ms and a notification has been scheduled 10ms ago (ie, it
+ // should trigger in no less than 6ms), then calling schedule will ensure that
+ // the only notification that arrives will happen in (at least) 16ms from now.
+ void Schedule();
+
+ // Cancel any previously scheduled runs.
+ void Cancel();
+
+ protected:
+ // Virtual for testing.
+ virtual base::TimeTicks Now() const;
+
+ private:
+ void NotifyIfTime();
+
+ base::SequencedTaskRunner* task_runner_;
+ base::Closure closure_;
+ base::TimeDelta delay_;
+ base::TimeTicks next_notification_time_;
+ bool notification_pending_;
+
+ base::WeakPtrFactory<DelayedUniqueNotifier> weak_ptr_factory_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
diff --git a/cc/base/delayed_unique_notifier_unittest.cc b/cc/base/delayed_unique_notifier_unittest.cc
new file mode 100644
index 0000000..113146a
--- /dev/null
+++ b/cc/base/delayed_unique_notifier_unittest.cc
@@ -0,0 +1,257 @@
+// 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 <deque>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/test/test_pending_task.h"
+#include "base/test/test_simple_task_runner.h"
+#include "cc/base/delayed_unique_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class TestNotifier : public DelayedUniqueNotifier {
+ public:
+ TestNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure,
+ const base::TimeDelta& delay)
+ : DelayedUniqueNotifier(task_runner, closure, delay) {}
+ virtual ~TestNotifier() {}
+
+ // Overridden from DelayedUniqueNotifier:
+ virtual base::TimeTicks Now() const OVERRIDE { return now_; }
+
+ void SetNow(base::TimeTicks now) { now_ = now; }
+
+ private:
+ base::TimeTicks now_;
+};
+
+class DelayedUniqueNotifierTest : public testing::Test {
+ public:
+ DelayedUniqueNotifierTest() : notification_count_(0) {}
+
+ virtual void SetUp() OVERRIDE {
+ notification_count_ = 0;
+ task_runner_ = make_scoped_refptr(new base::TestSimpleTaskRunner);
+ }
+
+ void Notify() { ++notification_count_; }
+
+ int NotificationCount() const { return notification_count_; }
+
+ std::deque<base::TestPendingTask> TakePendingTasks() {
+ std::deque<base::TestPendingTask> tasks = task_runner_->GetPendingTasks();
+ task_runner_->ClearPendingTasks();
+ return tasks;
+ }
+
+ protected:
+ int notification_count_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+};
+
+TEST_F(DelayedUniqueNotifierTest, ZeroDelay) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(0);
+ TestNotifier notifier(
+ task_runner_,
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Basic schedule for |delay| from now.
+ base::TimeTicks schedule_time =
+ base::TimeTicks() + base::TimeDelta::FromInternalValue(10);
+
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+
+ // 5 schedules should result in only one run.
+ for (int i = 0; i < 5; ++i)
+ notifier.Schedule();
+
+ tasks = TakePendingTasks();
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ tasks[0].task.Run();
+ EXPECT_EQ(2, NotificationCount());
+}
+
+TEST_F(DelayedUniqueNotifierTest, SmallDelay) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+ TestNotifier notifier(
+ task_runner_,
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Basic schedule for |delay| from now (now: 30, run time: 50).
+ base::TimeTicks schedule_time =
+ base::TimeTicks() + base::TimeDelta::FromInternalValue(30);
+
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // It's not yet time to run, so we expect no notifications.
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+
+ tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ // Now the time should be delay minus whatever the value of now happens to be
+ // (now: 30, run time: 50).
+ base::TimeTicks scheduled_run_time = notifier.Now() + delay;
+ base::TimeTicks scheduled_delay =
+ base::TimeTicks() + (scheduled_run_time - notifier.Now());
+ EXPECT_EQ(scheduled_delay, tasks[0].GetTimeToRun());
+
+ // Move closer to the run time (time: 49, run time: 50).
+ notifier.SetNow(notifier.Now() + base::TimeDelta::FromInternalValue(19));
+
+ // It's not yet time to run, so we expect no notifications.
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+
+ tasks = TakePendingTasks();
+ ASSERT_EQ(1u, tasks.size());
+
+ // Now the time should be delay minus whatever the value of now happens to be.
+ scheduled_delay = base::TimeTicks() + (scheduled_run_time - notifier.Now());
+ EXPECT_EQ(scheduled_delay, tasks[0].GetTimeToRun());
+
+ // Move to exactly the run time (time: 50, run time: 50).
+ notifier.SetNow(notifier.Now() + base::TimeDelta::FromInternalValue(1));
+
+ // It's time to run!
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+
+ tasks = TakePendingTasks();
+ EXPECT_EQ(0u, tasks.size());
+}
+
+TEST_F(DelayedUniqueNotifierTest, RescheduleDelay) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+ TestNotifier notifier(
+ task_runner_,
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ base::TimeTicks schedule_time;
+ // Move time 19 units forward and reschedule, expecting that we still need to
+ // run in |delay| time and we don't get a notification.
+ for (int i = 0; i < 10; ++i) {
+ EXPECT_EQ(0, NotificationCount());
+
+ // Move time forward 19 units.
+ schedule_time = notifier.Now() + base::TimeDelta::FromInternalValue(19);
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // It's not yet time to run, so we expect no notifications.
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+ }
+
+ // Move time forward 20 units, expecting a notification.
+ schedule_time = notifier.Now() + base::TimeDelta::FromInternalValue(20);
+ notifier.SetNow(schedule_time);
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Time to run!
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+}
+
+TEST_F(DelayedUniqueNotifierTest, Cancel) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+ TestNotifier notifier(
+ task_runner_,
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Schedule for |delay| seconds from now.
+ base::TimeTicks schedule_time =
+ notifier.Now() + base::TimeDelta::FromInternalValue(10);
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+
+ // Cancel the run.
+ notifier.Cancel();
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Time to run, but a canceled task!
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+
+ tasks = TakePendingTasks();
+ EXPECT_EQ(0u, tasks.size());
+
+ notifier.Schedule();
+ tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Advance the time.
+ notifier.SetNow(notifier.Now() + delay);
+
+ // This should run since it wasn't scheduled.
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+
+ for (int i = 0; i < 10; ++i)
+ notifier.Schedule();
+
+ notifier.Cancel();
+ tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Time to run, but a canceled task!
+ notifier.SetNow(notifier.Now() + delay);
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+
+ tasks = TakePendingTasks();
+ EXPECT_EQ(0u, tasks.size());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/base/unique_notifier.cc b/cc/base/unique_notifier.cc
new file mode 100644
index 0000000..0656c6d
--- /dev/null
+++ b/cc/base/unique_notifier.cc
@@ -0,0 +1,42 @@
+// 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 "cc/base/unique_notifier.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+
+namespace cc {
+
+UniqueNotifier::UniqueNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure)
+ : task_runner_(task_runner),
+ closure_(closure),
+ notification_pending_(false),
+ weak_ptr_factory_(this) {
+}
+
+UniqueNotifier::~UniqueNotifier() {
+}
+
+void UniqueNotifier::Schedule() {
+ if (notification_pending_)
+ return;
+
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UniqueNotifier::Notify, weak_ptr_factory_.GetWeakPtr()));
+ notification_pending_ = true;
+}
+
+void UniqueNotifier::Notify() {
+ // Note that the order here is important in case closure schedules another
+ // run.
+ notification_pending_ = false;
+ closure_.Run();
+}
+
+} // namespace cc
diff --git a/cc/base/unique_notifier.h b/cc/base/unique_notifier.h
new file mode 100644
index 0000000..4752f6f
--- /dev/null
+++ b/cc/base/unique_notifier.h
@@ -0,0 +1,44 @@
+// 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 CC_BASE_UNIQUE_NOTIFIER_H_
+#define CC_BASE_UNIQUE_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
+
+namespace cc {
+
+class CC_EXPORT UniqueNotifier {
+ public:
+ // Configure this notifier to issue the |closure| notification when scheduled.
+ UniqueNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure);
+
+ // Destroying the notifier will ensure that no further notifications will
+ // happen from this class.
+ ~UniqueNotifier();
+
+ // Schedule a notification to be run. If another notification is already
+ // pending, then only one notification will take place.
+ void Schedule();
+
+ private:
+ void Notify();
+
+ base::SequencedTaskRunner* task_runner_;
+ base::Closure closure_;
+ bool notification_pending_;
+
+ base::WeakPtrFactory<UniqueNotifier> weak_ptr_factory_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_UNIQUE_NOTIFIER_H_
diff --git a/cc/base/unique_notifier_unittest.cc b/cc/base/unique_notifier_unittest.cc
new file mode 100644
index 0000000..8c7b0d1
--- /dev/null
+++ b/cc/base/unique_notifier_unittest.cc
@@ -0,0 +1,62 @@
+// 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/bind_helpers.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/base/unique_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class UniqueNotifierTest : public testing::Test {
+ public:
+ UniqueNotifierTest() : notification_count_(0) {}
+
+ virtual void SetUp() OVERRIDE { ResetNotificationCount(); }
+
+ void Notify() { ++notification_count_; }
+
+ int NotificationCount() const { return notification_count_; }
+
+ void ResetNotificationCount() { notification_count_ = 0; }
+
+ protected:
+ int notification_count_;
+};
+
+TEST_F(UniqueNotifierTest, Schedule) {
+ {
+ UniqueNotifier notifier(
+ base::MessageLoopProxy::current(),
+ base::Bind(&UniqueNotifierTest::Notify, base::Unretained(this)));
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Basic schedule should result in a run.
+ notifier.Schedule();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, NotificationCount());
+
+ // Multiple schedules should only result in one run.
+ for (int i = 0; i < 5; ++i)
+ notifier.Schedule();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, NotificationCount());
+
+ notifier.Schedule();
+ }
+
+ // Notifier went out of scope, so we don't expect to get a notification.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, NotificationCount());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/cc.gyp b/cc/cc.gyp
index d05de57..2539323b 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -64,6 +64,8 @@
'animation/transform_operations.cc',
'animation/transform_operations.h',
'base/completion_event.h',
+ 'base/delayed_unique_notifier.cc',
+ 'base/delayed_unique_notifier.h',
'base/invalidation_region.cc',
'base/invalidation_region.h',
'base/latency_info_swap_promise.cc',
@@ -87,6 +89,8 @@
'base/switches.h',
'base/tiling_data.cc',
'base/tiling_data.h',
+ 'base/unique_notifier.cc',
+ 'base/unique_notifier.h',
'base/util.h',
'debug/benchmark_instrumentation.cc',
'debug/benchmark_instrumentation.h',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 857d098..119403a 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -13,12 +13,14 @@
'animation/scrollbar_animation_controller_linear_fade_unittest.cc',
'animation/scrollbar_animation_controller_thinning_unittest.cc',
'animation/transform_operations_unittest.cc',
+ 'base/delayed_unique_notifier_unittest.cc',
'base/float_quad_unittest.cc',
'base/math_util_unittest.cc',
'base/region_unittest.cc',
'base/rolling_time_delta_history_unittest.cc',
'base/scoped_ptr_vector_unittest.cc',
'base/tiling_data_unittest.cc',
+ 'base/unique_notifier_unittest.cc',
'base/util_unittest.cc',
'debug/micro_benchmark_controller_unittest.cc',
'input/top_controls_manager_unittest.cc',