summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchrisha <chrisha@chromium.org>2015-08-24 09:04:23 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-24 16:05:04 +0000
commitdfdde660bba559dec7b5baffb5544e634447e0dc (patch)
treedecda15808b86513e7345df19e794411c50e3982
parent4fb74d48d8d3001177fdfa0f2455d39be1363552 (diff)
downloadchromium_src-dfdde660bba559dec7b5baffb5544e634447e0dc.zip
chromium_src-dfdde660bba559dec7b5baffb5544e634447e0dc.tar.gz
chromium_src-dfdde660bba559dec7b5baffb5544e634447e0dc.tar.bz2
Create MemoryPressure component.
This is the first CL in the refactoring of base/memory/memory_pressure_*. Futher CLs will introduce calculators for each platform, the notification and UMA reporting logic. BUG=520962 Review URL: https://codereview.chromium.org/1293873002 Cr-Commit-Position: refs/heads/master@{#345081}
-rw-r--r--components/BUILD.gn2
-rw-r--r--components/OWNERS4
-rw-r--r--components/components.gyp1
-rw-r--r--components/components_tests.gyp8
-rw-r--r--components/memory_pressure.gypi29
-rw-r--r--components/memory_pressure/BUILD.gn37
-rw-r--r--components/memory_pressure/DEPS3
-rw-r--r--components/memory_pressure/OWNERS3
-rw-r--r--components/memory_pressure/direct_memory_pressure_calculator.cc18
-rw-r--r--components/memory_pressure/direct_memory_pressure_calculator.h77
-rw-r--r--components/memory_pressure/direct_memory_pressure_calculator_win.cc104
-rw-r--r--components/memory_pressure/direct_memory_pressure_calculator_win_unittest.cc175
-rw-r--r--components/memory_pressure/filtered_memory_pressure_calculator.cc91
-rw-r--r--components/memory_pressure/filtered_memory_pressure_calculator.h79
-rw-r--r--components/memory_pressure/filtered_memory_pressure_calculator_unittest.cc298
-rw-r--r--components/memory_pressure/memory_pressure_calculator.h32
-rw-r--r--components/memory_pressure/memory_pressure_listener.cc61
-rw-r--r--components/memory_pressure/memory_pressure_listener.h92
-rw-r--r--components/memory_pressure/test_memory_pressure_calculator.cc38
-rw-r--r--components/memory_pressure/test_memory_pressure_calculator.h49
20 files changed, 1201 insertions, 0 deletions
diff --git a/components/BUILD.gn b/components/BUILD.gn
index dde6837..7d2729b 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -61,6 +61,7 @@ group("all_components") {
"//components/language_usage_metrics",
"//components/leveldb_proto",
"//components/login",
+ "//components/memory_pressure",
"//components/metrics",
"//components/mime_util",
"//components/navigation_interception",
@@ -333,6 +334,7 @@ test("components_unittests") {
"//components/keyed_service/core:unit_tests",
"//components/leveldb_proto:unit_tests",
"//components/login:unit_tests",
+ "//components/memory_pressure:unit_tests",
"//components/metrics:unit_tests",
"//components/mime_util:unit_tests",
"//components/offline_pages:unit_tests",
diff --git a/components/OWNERS b/components/OWNERS
index 47bd329..f4f3813 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -150,6 +150,10 @@ per-file login*=dzhioev@chromium.org
per-file login*=nkostylev@chromium.org
per-file login*=achuith@chromium.org
+per-file memory_pressure*=chrisha@chromium.org
+per-file memory_pressure*=shrike@chromium.org
+per-file memory_pressure*=skuhne@chromium.org
+
per-file metrics*=asvitkine@chromium.org
per-file metrics*=mpearson@chromium.org
per-file metrics*=jar@chromium.org
diff --git a/components/components.gyp b/components/components.gyp
index 630c5e6..9990cbf 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -42,6 +42,7 @@
'language_usage_metrics.gypi',
'leveldb_proto.gypi',
'login.gypi',
+ 'memory_pressure.gypi',
'metrics.gypi',
'navigation_metrics.gypi',
'network_hints.gypi',
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 4e4a6bc..f40253e 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -298,6 +298,12 @@
'login_unittest_sources': [
'login/screens/screen_context_unittest.cc',
],
+ 'memory_pressure_unittest_sources': [
+ 'memory_pressure/direct_memory_pressure_calculator_win_unittest.cc',
+ 'memory_pressure/filtered_memory_pressure_calculator_unittest.cc',
+ 'memory_pressure/test_memory_pressure_calculator.cc',
+ 'memory_pressure/test_memory_pressure_calculator.h',
+ ],
'metrics_unittest_sources': [
'metrics/call_stack_profile_metrics_provider_unittest.cc',
'metrics/compression_utils_unittest.cc',
@@ -763,6 +769,7 @@
'<@(language_usage_metrics_unittest_sources)',
'<@(leveldb_proto_unittest_sources)',
'<@(login_unittest_sources)',
+ '<@(memory_pressure_unittest_sources)',
'<@(metrics_unittest_sources)',
'<@(mime_util_unittest_sources)',
'<@(network_time_unittest_sources)',
@@ -868,6 +875,7 @@
'components.gyp:leveldb_proto',
'components.gyp:leveldb_proto_test_support',
'components.gyp:login',
+ 'components.gyp:memory_pressure',
'components.gyp:metrics',
'components.gyp:metrics_gpu',
'components.gyp:metrics_net',
diff --git a/components/memory_pressure.gypi b/components/memory_pressure.gypi
new file mode 100644
index 0000000..40fb726
--- /dev/null
+++ b/components/memory_pressure.gypi
@@ -0,0 +1,29 @@
+# Copyright 2015 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.
+
+{
+ 'targets': [
+ {
+ # GN version: //components/memory_pressure
+ 'target_name': 'memory_pressure',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'sources': [
+ 'memory_pressure/direct_memory_pressure_calculator.cc',
+ 'memory_pressure/direct_memory_pressure_calculator.h',
+ 'memory_pressure/direct_memory_pressure_calculator_win.cc',
+ 'memory_pressure/filtered_memory_pressure_calculator.cc',
+ 'memory_pressure/filtered_memory_pressure_calculator.h',
+ 'memory_pressure/memory_pressure_calculator.h',
+ 'memory_pressure/memory_pressure_listener.cc',
+ 'memory_pressure/memory_pressure_listener.h',
+ ],
+ },
+ ],
+}
diff --git a/components/memory_pressure/BUILD.gn b/components/memory_pressure/BUILD.gn
new file mode 100644
index 0000000..3828f48
--- /dev/null
+++ b/components/memory_pressure/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2015 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.
+
+# GYP version: components/memory_pressure.gypi:memory_pressure
+source_set("memory_pressure") {
+ sources = [
+ "direct_memory_pressure_calculator.cc",
+ "direct_memory_pressure_calculator.h",
+ "direct_memory_pressure_calculator_win.cc",
+ "filtered_memory_pressure_calculator.cc",
+ "filtered_memory_pressure_calculator.h",
+ "memory_pressure_calculator.h",
+ "memory_pressure_listener.cc",
+ "memory_pressure_listener.h",
+ ]
+
+ deps = [
+ "//base",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "direct_memory_pressure_calculator_win_unittest.cc",
+ "filtered_memory_pressure_calculator_unittest.cc",
+ "test_memory_pressure_calculator.cc",
+ "test_memory_pressure_calculator.h",
+ ]
+
+ deps = [
+ ":memory_pressure",
+ "//base/test:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/components/memory_pressure/DEPS b/components/memory_pressure/DEPS
new file mode 100644
index 0000000..5cd0867
--- /dev/null
+++ b/components/memory_pressure/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+base",
+]
diff --git a/components/memory_pressure/OWNERS b/components/memory_pressure/OWNERS
new file mode 100644
index 0000000..1f45750
--- /dev/null
+++ b/components/memory_pressure/OWNERS
@@ -0,0 +1,3 @@
+chrisha@chromium.org
+shrike@chromium.org
+skuhne@chromium.org
diff --git a/components/memory_pressure/direct_memory_pressure_calculator.cc b/components/memory_pressure/direct_memory_pressure_calculator.cc
new file mode 100644
index 0000000..88b5794
--- /dev/null
+++ b/components/memory_pressure/direct_memory_pressure_calculator.cc
@@ -0,0 +1,18 @@
+// Copyright 2015 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 "components/memory_pressure/direct_memory_pressure_calculator.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+bool DirectMemoryPressureCalculator::GetSystemMemoryInfo(
+ base::SystemMemoryInfoKB* mem_info) const {
+ return base::GetSystemMemoryInfo(mem_info);
+}
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
diff --git a/components/memory_pressure/direct_memory_pressure_calculator.h b/components/memory_pressure/direct_memory_pressure_calculator.h
new file mode 100644
index 0000000..98d3e16
--- /dev/null
+++ b/components/memory_pressure/direct_memory_pressure_calculator.h
@@ -0,0 +1,77 @@
+// Copyright 2015 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 COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_H_
+#define COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_H_
+
+#include "components/memory_pressure/memory_pressure_calculator.h"
+
+#include "base/process/process_metrics.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+// OS-specific implementation of MemoryPressureCalculator. This is only defined
+// and used on platforms that do not have native memory pressure signals
+// (ChromeOS, Linux, Windows). OSes that do have native signals simply hook into
+// the appropriate subsystem (Android, Mac OS X).
+class DirectMemoryPressureCalculator : public MemoryPressureCalculator {
+ public:
+#if defined(OS_WIN)
+ // Exposed for unittesting. See .cc file for detailed discussion of these
+ // constants.
+ static const int kLargeMemoryThresholdMb;
+ static const int kSmallMemoryDefaultModerateThresholdMb;
+ static const int kSmallMemoryDefaultCriticalThresholdMb;
+ static const int kLargeMemoryDefaultModerateThresholdMb;
+ static const int kLargeMemoryDefaultCriticalThresholdMb;
+#endif
+
+ // Default constructor. Will choose thresholds automatically based on the
+ // actual amount of system memory installed.
+ DirectMemoryPressureCalculator();
+
+ // Constructor with explicit memory thresholds. These represent the amount of
+ // free memory below which the applicable memory pressure state applies.
+ DirectMemoryPressureCalculator(int moderate_threshold_mb,
+ int critical_threshold_mb);
+
+ virtual ~DirectMemoryPressureCalculator() {}
+
+ // Calculates the current pressure level.
+ MemoryPressureLevel CalculateCurrentPressureLevel() override;
+
+#if defined(OS_WIN)
+ int moderate_threshold_mb() const { return moderate_threshold_mb_; }
+ int critical_threshold_mb() const { return critical_threshold_mb_; }
+#endif
+
+ private:
+ friend class TestDirectMemoryPressureCalculator;
+
+ // Gets system memory status. This is virtual as a unittesting hook and by
+ // default this invokes base::GetSystemMemoryInfo.
+ virtual bool GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) const;
+
+#if defined(OS_WIN)
+ // Uses GetSystemMemoryInfo to automatically infer appropriate values for
+ // moderate_threshold_mb_ and critical_threshold_mb_.
+ void InferThresholds();
+
+ // Threshold amounts of available memory that trigger pressure levels. See
+ // memory_pressure_monitor_win.cc for a discussion of reasonable values for
+ // these.
+ int moderate_threshold_mb_;
+ int critical_threshold_mb_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(DirectMemoryPressureCalculator);
+};
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
+
+#endif // COMPONENTS_MEMORY_PRESSURE_DIRECT_MEMORY_PRESSURE_CALCULATOR_H_
diff --git a/components/memory_pressure/direct_memory_pressure_calculator_win.cc b/components/memory_pressure/direct_memory_pressure_calculator_win.cc
new file mode 100644
index 0000000..25d7d7b
--- /dev/null
+++ b/components/memory_pressure/direct_memory_pressure_calculator_win.cc
@@ -0,0 +1,104 @@
+// Copyright 2015 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 "components/memory_pressure/direct_memory_pressure_calculator.h"
+
+namespace memory_pressure {
+
+namespace {
+
+static const int kKBperMB = 1024;
+
+} // namespace
+
+// A system is considered 'high memory' if it has more than 1.5GB of system
+// memory available for use by the memory manager (not reserved for hardware
+// and drivers). This is a fuzzy version of the ~2GB discussed below.
+const int DirectMemoryPressureCalculator::kLargeMemoryThresholdMb = 1536;
+
+// These are the default thresholds used for systems with < ~2GB of physical
+// memory. Such systems have been observed to always maintain ~100MB of
+// available memory, paging until that is the case. To try to avoid paging a
+// threshold slightly above this is chosen. The moderate threshold is slightly
+// less grounded in reality and chosen as 2.5x critical.
+const int
+ DirectMemoryPressureCalculator::kSmallMemoryDefaultModerateThresholdMb =
+ 500;
+const int
+ DirectMemoryPressureCalculator::kSmallMemoryDefaultCriticalThresholdMb =
+ 200;
+
+// These are the default thresholds used for systems with >= ~2GB of physical
+// memory. Such systems have been observed to always maintain ~300MB of
+// available memory, paging until that is the case.
+const int
+ DirectMemoryPressureCalculator::kLargeMemoryDefaultModerateThresholdMb =
+ 1000;
+const int
+ DirectMemoryPressureCalculator::kLargeMemoryDefaultCriticalThresholdMb =
+ 400;
+
+DirectMemoryPressureCalculator::DirectMemoryPressureCalculator()
+ : moderate_threshold_mb_(0), critical_threshold_mb_(0) {
+ InferThresholds();
+}
+
+DirectMemoryPressureCalculator::DirectMemoryPressureCalculator(
+ int moderate_threshold_mb,
+ int critical_threshold_mb)
+ : moderate_threshold_mb_(moderate_threshold_mb),
+ critical_threshold_mb_(critical_threshold_mb) {
+ DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_);
+ DCHECK_LE(0, critical_threshold_mb_);
+}
+
+DirectMemoryPressureCalculator::MemoryPressureLevel
+DirectMemoryPressureCalculator::CalculateCurrentPressureLevel() {
+ base::SystemMemoryInfoKB mem_info = {};
+ if (!GetSystemMemoryInfo(&mem_info))
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+
+ // How much system memory is actively available for use right now, in MBs.
+ int phys_free = mem_info.free / kKBperMB;
+
+ // TODO(chrisha): This should eventually care about address space pressure,
+ // but the browser process (where this is running) effectively never runs out
+ // of address space. Renderers occasionally do, but it does them no good to
+ // have the browser process monitor address space pressure. Long term,
+ // renderers should run their own address space pressure monitors and act
+ // accordingly, with the browser making cross-process decisions based on
+ // system memory pressure.
+
+ // Determine if the physical memory is under critical memory pressure.
+ if (phys_free <= critical_threshold_mb_)
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
+
+ // Determine if the physical memory is under moderate memory pressure.
+ if (phys_free <= moderate_threshold_mb_)
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
+
+ // No memory pressure was detected.
+ return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+}
+
+void DirectMemoryPressureCalculator::InferThresholds() {
+ // Determine if the memory installed is 'large' or 'small'. Default to 'large'
+ // on failure, which uses more conservative thresholds.
+ bool large_memory = true;
+ base::SystemMemoryInfoKB mem_info = {};
+ if (GetSystemMemoryInfo(&mem_info)) {
+ large_memory = mem_info.total / kKBperMB >=
+ DirectMemoryPressureCalculator::kLargeMemoryThresholdMb;
+ }
+
+ if (large_memory) {
+ moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb;
+ critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb;
+ } else {
+ moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb;
+ critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb;
+ }
+}
+
+} // namespace memory_pressure
diff --git a/components/memory_pressure/direct_memory_pressure_calculator_win_unittest.cc b/components/memory_pressure/direct_memory_pressure_calculator_win_unittest.cc
new file mode 100644
index 0000000..c4aa962
--- /dev/null
+++ b/components/memory_pressure/direct_memory_pressure_calculator_win_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2015 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 "components/memory_pressure/direct_memory_pressure_calculator.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace memory_pressure {
+
+namespace {
+
+static const int kKBperMB = 1024;
+
+} // namespace
+
+// This is out of the anonymous namespace space because it is a friend of
+// DirectMemoryPressureCalculator.
+class TestDirectMemoryPressureCalculator
+ : public DirectMemoryPressureCalculator {
+ public:
+ explicit TestDirectMemoryPressureCalculator(bool large_memory)
+ : DirectMemoryPressureCalculator(20, 10) {
+ // The values passed to the MemoryPressureCalculator constructor are dummy
+ // values that are immediately overwritten by InferTresholds.
+
+ // Generate a plausible amount of memory.
+ mem_info_.total = GenerateTotalMemoryMb(large_memory) * kKBperMB;
+
+ // Run InferThresholds using the test fixture's GetSystemMemoryStatus.
+ InferThresholds();
+ }
+
+ TestDirectMemoryPressureCalculator(int total_memory_mb,
+ int moderate_threshold_mb,
+ int critical_threshold_mb)
+ : DirectMemoryPressureCalculator(moderate_threshold_mb,
+ critical_threshold_mb) {
+ mem_info_.total = total_memory_mb * kKBperMB;
+ }
+
+ // Generates an amount of total memory that is consistent with the requested
+ // memory model.
+ static int GenerateTotalMemoryMb(bool large_memory) {
+ int total_mb = 64;
+ while (total_mb < kLargeMemoryThresholdMb)
+ total_mb *= 2;
+ if (large_memory)
+ return total_mb * 2;
+ return total_mb / 2;
+ }
+
+ // Sets up the memory status to reflect the provided absolute memory left.
+ void SetMemoryFree(int phys_left_mb) {
+ // |total| is set in the constructor and not modified.
+
+ // Set the amount of free memory.
+ mem_info_.free = phys_left_mb * kKBperMB;
+ DCHECK_LT(mem_info_.free, mem_info_.total);
+ }
+
+ void SetNone() { SetMemoryFree(moderate_threshold_mb() + 1); }
+
+ void SetModerate() { SetMemoryFree(moderate_threshold_mb() - 1); }
+
+ void SetCritical() { SetMemoryFree(critical_threshold_mb() - 1); }
+
+ private:
+ bool GetSystemMemoryInfo(base::SystemMemoryInfoKB* mem_info) const override {
+ // Simply copy the memory information set by the test fixture.
+ *mem_info = mem_info_;
+ return true;
+ }
+
+ base::SystemMemoryInfoKB mem_info_;
+};
+
+class DirectMemoryPressureCalculatorTest : public testing::Test {
+ public:
+ void CalculateCurrentPressureLevelTest(
+ TestDirectMemoryPressureCalculator* calc) {
+ int mod = calc->moderate_threshold_mb();
+ calc->SetMemoryFree(mod + 1);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ calc->CalculateCurrentPressureLevel());
+
+ calc->SetNone();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ calc->CalculateCurrentPressureLevel());
+
+ calc->SetMemoryFree(mod);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ calc->CalculateCurrentPressureLevel());
+
+ calc->SetMemoryFree(mod - 1);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ calc->CalculateCurrentPressureLevel());
+
+ int crit = calc->critical_threshold_mb();
+ calc->SetMemoryFree(crit + 1);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ calc->CalculateCurrentPressureLevel());
+
+ calc->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ calc->CalculateCurrentPressureLevel());
+
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ calc->CalculateCurrentPressureLevel());
+
+ calc->SetMemoryFree(crit);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ calc->CalculateCurrentPressureLevel());
+
+ calc->SetMemoryFree(crit - 1);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ calc->CalculateCurrentPressureLevel());
+
+ calc->SetCritical();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ calc->CalculateCurrentPressureLevel());
+ }
+};
+
+// Tests the fundamental direct calculation of memory pressure with automatic
+// small-memory thresholds.
+TEST_F(DirectMemoryPressureCalculatorTest,
+ CalculateCurrentMemoryPressureLevelSmall) {
+ static const int kModerateMb =
+ DirectMemoryPressureCalculator::kSmallMemoryDefaultModerateThresholdMb;
+ static const int kCriticalMb =
+ DirectMemoryPressureCalculator::kSmallMemoryDefaultCriticalThresholdMb;
+
+ TestDirectMemoryPressureCalculator calc(false); // Small-memory model.
+
+ EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
+ EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
+
+ ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
+}
+
+// Tests the fundamental direct calculation of memory pressure with automatic
+// large-memory thresholds.
+TEST_F(DirectMemoryPressureCalculatorTest,
+ CalculateCurrentMemoryPressureLevelLarge) {
+ static const int kModerateMb =
+ DirectMemoryPressureCalculator::kLargeMemoryDefaultModerateThresholdMb;
+ static const int kCriticalMb =
+ DirectMemoryPressureCalculator::kLargeMemoryDefaultCriticalThresholdMb;
+
+ TestDirectMemoryPressureCalculator calc(true); // Large-memory model.
+
+ EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
+ EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
+
+ ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
+}
+
+// Tests the fundamental direct calculation of memory pressure with manually
+// specified threshold levels.
+TEST_F(DirectMemoryPressureCalculatorTest,
+ CalculateCurrentMemoryPressureLevelCustom) {
+ static const int kSystemMb = 512;
+ static const int kModerateMb = 256;
+ static const int kCriticalMb = 128;
+
+ TestDirectMemoryPressureCalculator calc(kSystemMb, kModerateMb, kCriticalMb);
+
+ EXPECT_EQ(kModerateMb, calc.moderate_threshold_mb());
+ EXPECT_EQ(kCriticalMb, calc.critical_threshold_mb());
+
+ ASSERT_NO_FATAL_FAILURE(CalculateCurrentPressureLevelTest(&calc));
+}
+
+} // namespace memory_pressure
diff --git a/components/memory_pressure/filtered_memory_pressure_calculator.cc b/components/memory_pressure/filtered_memory_pressure_calculator.cc
new file mode 100644
index 0000000..9f5aa9d
--- /dev/null
+++ b/components/memory_pressure/filtered_memory_pressure_calculator.cc
@@ -0,0 +1,91 @@
+// Copyright 2015 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 "components/memory_pressure/filtered_memory_pressure_calculator.h"
+
+#include "base/time/default_tick_clock.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+// 100ms (10Hz) allows a relatively fast respsonse time for rapidly increasing
+// memory usage, but limits the amount of work done in the calculator.
+const int FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs = 100;
+
+// These values were experimentally obtained during the initial ChromeOS only
+// implementation of this feature. By spending a significant cooldown period in
+// at a higher pressure level more time is dedicated to freeing resources and
+// less churn occurs in the MemoryPressureListener event stream.
+const int FilteredMemoryPressureCalculator::kCriticalPressureCooldownPeriodMs =
+ 5000;
+const int FilteredMemoryPressureCalculator::kModeratePressureCooldownPeriodMs =
+ 5000;
+
+FilteredMemoryPressureCalculator::FilteredMemoryPressureCalculator(
+ scoped_ptr<MemoryPressureCalculator> pressure_calculator)
+ : tick_clock_(new base::DefaultTickClock()),
+ pressure_calculator_(pressure_calculator.Pass()),
+ current_pressure_level_(
+ MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
+ samples_taken_(false),
+ cooldown_in_progress_(false) {}
+
+FilteredMemoryPressureCalculator::MemoryPressureLevel
+FilteredMemoryPressureCalculator::CalculateCurrentPressureLevel() {
+ base::TimeTicks now = tick_clock_->NowTicks();
+
+ // If its too soon to take a sample then return the precalculated value.
+ if (samples_taken_ &&
+ (now - last_sample_time_) <
+ base::TimeDelta::FromMilliseconds(kMinimumTimeBetweenSamplesMs)) {
+ return current_pressure_level_;
+ }
+
+ // Take a sample.
+ samples_taken_ = true;
+ last_sample_time_ = now;
+ MemoryPressureLevel level =
+ pressure_calculator_->CalculateCurrentPressureLevel();
+
+ // The pressure hasn't changed or has gone up. In either case this is the end
+ // of a cooldown period if one was in progress.
+ if (level >= current_pressure_level_) {
+ cooldown_in_progress_ = false;
+ current_pressure_level_ = level;
+ return level;
+ }
+
+ // The pressure has gone down, so apply cooldown hysteresis.
+ if (cooldown_in_progress_) {
+ cooldown_high_tide_ = std::max(cooldown_high_tide_, level);
+
+ // Get the cooldown period for the current level.
+ int cooldown_ms = kCriticalPressureCooldownPeriodMs;
+ if (current_pressure_level_ ==
+ MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE) {
+ cooldown_ms = kModeratePressureCooldownPeriodMs;
+ }
+ base::TimeDelta cooldown_period =
+ base::TimeDelta::FromMilliseconds(cooldown_ms);
+
+ if (now - cooldown_start_time_ >= cooldown_period) {
+ // The cooldown has completed successfully, so transition the pressure
+ // level.
+ cooldown_in_progress_ = false;
+ current_pressure_level_ = cooldown_high_tide_;
+ }
+ } else {
+ // Start a new cooldown period.
+ cooldown_in_progress_ = true;
+ cooldown_start_time_ = now;
+ cooldown_high_tide_ = level;
+ }
+
+ return current_pressure_level_;
+}
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
diff --git a/components/memory_pressure/filtered_memory_pressure_calculator.h b/components/memory_pressure/filtered_memory_pressure_calculator.h
new file mode 100644
index 0000000..9d9b902
--- /dev/null
+++ b/components/memory_pressure/filtered_memory_pressure_calculator.h
@@ -0,0 +1,79 @@
+// Copyright 2015 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 COMPONENTS_MEMORY_PRESSURE_FILTERED_MEMORY_PRESSURE_CALCULATOR_H_
+#define COMPONENTS_MEMORY_PRESSURE_FILTERED_MEMORY_PRESSURE_CALCULATOR_H_
+
+#include "base/time/tick_clock.h"
+#include "components/memory_pressure/memory_pressure_calculator.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+// A utility class that provides rate-limiting and hysteresis on raw memory
+// pressure calculations. This is identical across all platforms, but only used
+// on those that do not have native memory pressure signals.
+class FilteredMemoryPressureCalculator : public MemoryPressureCalculator {
+ public:
+ // The minimum time that must pass between successive polls. This enforces an
+ // upper bound on the rate of calls to the contained MemoryPressureCalculator.
+ static const int kMinimumTimeBetweenSamplesMs;
+
+ // The cooldown period when transitioning from critical to moderate/no memory
+ // pressure, or from moderate to none.
+ static const int kCriticalPressureCooldownPeriodMs;
+ static const int kModeratePressureCooldownPeriodMs;
+
+ explicit FilteredMemoryPressureCalculator(
+ scoped_ptr<MemoryPressureCalculator> pressure_calculator);
+ ~FilteredMemoryPressureCalculator() {}
+
+ // Calculates the current pressure level.
+ MemoryPressureLevel CalculateCurrentPressureLevel() override;
+
+ // Testing seam for configuring the tick clock in use.
+ void set_tick_clock(scoped_ptr<base::TickClock> tick_clock) {
+ tick_clock_ = tick_clock.Pass();
+ }
+
+ // Accessors for unittesting.
+ bool cooldown_in_progress() const { return cooldown_in_progress_; }
+ base::TimeTicks cooldown_start_time() const { return cooldown_start_time_; }
+ MemoryPressureLevel cooldown_high_tide() const { return cooldown_high_tide_; }
+
+ private:
+ friend class TestFilteredMemoryPressureCalculator;
+
+ // The delegate tick clock. This is settable as a testing seam.
+ scoped_ptr<base::TickClock> tick_clock_;
+
+ // The delegate pressure calculator. Provided by the constructor.
+ scoped_ptr<MemoryPressureCalculator> pressure_calculator_;
+
+ // The memory pressure currently being reported.
+ MemoryPressureLevel current_pressure_level_;
+
+ // The last time a sample was taken.
+ bool samples_taken_;
+ base::TimeTicks last_sample_time_;
+
+ // State of an ongoing cooldown period, if any. The high-tide line indicates
+ // the highest memory pressure level (*below* the current one) that was
+ // encountered during the cooldown period. This allows a cooldown to
+ // transition directly from critical to none if *no* moderate pressure signals
+ // were seen during the period, otherwise it forces it to pass through a
+ // moderate cooldown as well.
+ bool cooldown_in_progress_;
+ base::TimeTicks cooldown_start_time_;
+ MemoryPressureLevel cooldown_high_tide_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilteredMemoryPressureCalculator);
+};
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
+
+#endif // COMPONENTS_MEMORY_PRESSURE_FILTERED_MEMORY_PRESSURE_CALCULATOR_H_
diff --git a/components/memory_pressure/filtered_memory_pressure_calculator_unittest.cc b/components/memory_pressure/filtered_memory_pressure_calculator_unittest.cc
new file mode 100644
index 0000000..81b7723
--- /dev/null
+++ b/components/memory_pressure/filtered_memory_pressure_calculator_unittest.cc
@@ -0,0 +1,298 @@
+// Copyright 2015 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 "components/memory_pressure/filtered_memory_pressure_calculator.h"
+
+#include "base/test/simple_test_tick_clock.h"
+#include "components/memory_pressure/test_memory_pressure_calculator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+class FilteredMemoryPressureCalculatorTest : public testing::Test {
+ public:
+ FilteredMemoryPressureCalculatorTest()
+ : calculator_(nullptr), tick_clock_(nullptr) {}
+
+ void SetUp() override {
+ // Ownership of the calculator and tick clock are both passed to the filter.
+ calculator_ = new TestMemoryPressureCalculator();
+ tick_clock_ = new base::SimpleTestTickClock();
+ filter_.reset(new FilteredMemoryPressureCalculator(
+ scoped_ptr<MemoryPressureCalculator>(calculator_)));
+ filter_->set_tick_clock(scoped_ptr<base::TickClock>(tick_clock_));
+ }
+
+ void TearDown() override {
+ calculator_ = nullptr;
+ tick_clock_ = nullptr;
+ filter_.reset();
+ }
+
+ // Advances the tick clock by the given number of milliseconds.
+ void Tick(int ms) {
+ tick_clock_->Advance(base::TimeDelta::FromMilliseconds(ms));
+ }
+
+ // Delegates that are inject into the filter under test.
+ TestMemoryPressureCalculator* calculator_;
+ base::SimpleTestTickClock* tick_clock_;
+
+ // The object under test.
+ scoped_ptr<FilteredMemoryPressureCalculator> filter_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FilteredMemoryPressureCalculatorTest);
+};
+
+TEST_F(FilteredMemoryPressureCalculatorTest, FirstCallInvokesCalculator) {
+ calculator_->SetCritical();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+TEST_F(FilteredMemoryPressureCalculatorTest, CalculatorNotAlwaysInvoked) {
+ calculator_->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // Change the pressure reported by the caculator, but don't expect the filter
+ // to report the critical value until a minimum sampling period has passed.
+ // The level has to be increasing so that hysteresis doesn't come into play.
+ calculator_->SetCritical();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs / 2);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // A sufficient time has passed so now it should report the new level.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(2, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+TEST_F(FilteredMemoryPressureCalculatorTest, CooldownCriticalToModerate) {
+ calculator_->SetCritical();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // Initiate a cooldown period by jumping sufficiently far ahead for another
+ // sample.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(2, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(tick_clock_->NowTicks(), filter_->cooldown_start_time());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->cooldown_high_tide());
+
+ // Step another sample interval and it should still not have changed.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(3, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->cooldown_high_tide());
+
+ // Step the cooldown period and it should change state.
+ Tick(FilteredMemoryPressureCalculator::kCriticalPressureCooldownPeriodMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(4, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+TEST_F(FilteredMemoryPressureCalculatorTest,
+ CooldownCriticalToModerateViaNone) {
+ calculator_->SetCritical();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // Initiate a cooldown period by jumping sufficiently far ahead for another
+ // sample. First go directly to no memory pressure before passing back through
+ // moderate. The final result should be a moderate memory pressure.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetNone();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(2, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(tick_clock_->NowTicks(), filter_->cooldown_start_time());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->cooldown_high_tide());
+
+ // Step another sample interval and it should still not have changed.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(3, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->cooldown_high_tide());
+
+ // Step the cooldown period and it should change state.
+ Tick(FilteredMemoryPressureCalculator::kCriticalPressureCooldownPeriodMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(4, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+TEST_F(FilteredMemoryPressureCalculatorTest, CooldownCriticalToNone) {
+ calculator_->SetCritical();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // Initiate a cooldown period by jumping sufficiently far ahead for another
+ // sample.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetNone();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(2, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(tick_clock_->NowTicks(), filter_->cooldown_start_time());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->cooldown_high_tide());
+
+ // Step another sample interval and it should still not have changed.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(3, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->cooldown_high_tide());
+
+ // Step the cooldown period and it should change state.
+ Tick(FilteredMemoryPressureCalculator::kCriticalPressureCooldownPeriodMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(4, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+TEST_F(FilteredMemoryPressureCalculatorTest, CooldownModerateToNone) {
+ calculator_->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // Initiate a cooldown period by jumping sufficiently far ahead for another
+ // sample.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetNone();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(2, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(tick_clock_->NowTicks(), filter_->cooldown_start_time());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->cooldown_high_tide());
+
+ // Step another sample interval and it should still not have changed.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(3, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->cooldown_high_tide());
+
+ // Step the cooldown period and it should change state.
+ Tick(FilteredMemoryPressureCalculator::kModeratePressureCooldownPeriodMs);
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(4, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+TEST_F(FilteredMemoryPressureCalculatorTest, InterruptedCooldownModerate) {
+ calculator_->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // Initiate a cooldown period by jumping sufficiently far ahead for another
+ // sample.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetNone();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(2, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(tick_clock_->NowTicks(), filter_->cooldown_start_time());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->cooldown_high_tide());
+
+ // Step another sample interval and it should still not have changed. Since
+ // the pressure level has increased back to moderate it should also end the
+ // cooldown period.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(3, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+TEST_F(FilteredMemoryPressureCalculatorTest, InterruptedCooldownCritical) {
+ calculator_->SetModerate();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(1, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+
+ // Initiate a cooldown period by jumping sufficiently far ahead for another
+ // sample.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetNone();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(2, calculator_->calls());
+ EXPECT_TRUE(filter_->cooldown_in_progress());
+ EXPECT_EQ(tick_clock_->NowTicks(), filter_->cooldown_start_time());
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE,
+ filter_->cooldown_high_tide());
+
+ // Step another sample interval and it should still not have changed. Since
+ // the pressure level has increased to critical it ends the cooldown period
+ // and immediately reports the critical memory pressure.
+ Tick(FilteredMemoryPressureCalculator::kMinimumTimeBetweenSamplesMs);
+ calculator_->SetCritical();
+ EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL,
+ filter_->CalculateCurrentPressureLevel());
+ EXPECT_EQ(3, calculator_->calls());
+ EXPECT_FALSE(filter_->cooldown_in_progress());
+}
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
diff --git a/components/memory_pressure/memory_pressure_calculator.h b/components/memory_pressure/memory_pressure_calculator.h
new file mode 100644
index 0000000..b139efe
--- /dev/null
+++ b/components/memory_pressure/memory_pressure_calculator.h
@@ -0,0 +1,32 @@
+// Copyright 2015 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 COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_CALCULATOR_H_
+#define COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_CALCULATOR_H_
+
+#include "components/memory_pressure/memory_pressure_listener.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+// Interface for a utility class that calculates memory pressure. Only used on
+// platforms without native memory pressure signals.
+class MemoryPressureCalculator {
+ public:
+ using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel;
+
+ MemoryPressureCalculator() {}
+ virtual ~MemoryPressureCalculator() {}
+ virtual MemoryPressureLevel CalculateCurrentPressureLevel() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MemoryPressureCalculator);
+};
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
+
+#endif // COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_CALCULATOR_H_
diff --git a/components/memory_pressure/memory_pressure_listener.cc b/components/memory_pressure/memory_pressure_listener.cc
new file mode 100644
index 0000000..3d2fe1a
--- /dev/null
+++ b/components/memory_pressure/memory_pressure_listener.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 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 "components/memory_pressure/memory_pressure_listener.h"
+
+#include "base/lazy_instance.h"
+#include "base/observer_list_threadsafe.h"
+#include "base/trace_event/trace_event.h"
+
+namespace memory_pressure {
+
+namespace {
+
+// ObserverListThreadSafe is RefCountedThreadSafe, this traits is needed
+// to ensure the LazyInstance will hold a reference to it.
+struct LeakyLazyObserverListTraits
+ : base::internal::LeakyLazyInstanceTraits<
+ base::ObserverListThreadSafe<MemoryPressureListener>> {
+ static base::ObserverListThreadSafe<MemoryPressureListener>* New(
+ void* instance) {
+ base::ObserverListThreadSafe<MemoryPressureListener>* ret =
+ base::internal::LeakyLazyInstanceTraits<base::ObserverListThreadSafe<
+ MemoryPressureListener>>::New(instance);
+ // Leaky.
+ ret->AddRef();
+ return ret;
+ }
+};
+
+base::LazyInstance<base::ObserverListThreadSafe<MemoryPressureListener>,
+ LeakyLazyObserverListTraits> g_observers =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+MemoryPressureListener::MemoryPressureListener(
+ const MemoryPressureListener::MemoryPressureCallback& callback)
+ : callback_(callback) {
+ g_observers.Get().AddObserver(this);
+}
+
+MemoryPressureListener::~MemoryPressureListener() {
+ g_observers.Get().RemoveObserver(this);
+}
+
+void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) {
+ callback_.Run(memory_pressure_level);
+}
+
+// static
+void MemoryPressureListener::NotifyMemoryPressure(
+ MemoryPressureLevel memory_pressure_level) {
+ DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
+ TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure",
+ "level", memory_pressure_level);
+ g_observers.Get().Notify(FROM_HERE, &MemoryPressureListener::Notify,
+ memory_pressure_level);
+}
+
+} // namespace memory_pressure
diff --git a/components/memory_pressure/memory_pressure_listener.h b/components/memory_pressure/memory_pressure_listener.h
new file mode 100644
index 0000000..b99f8b2
--- /dev/null
+++ b/components/memory_pressure/memory_pressure_listener.h
@@ -0,0 +1,92 @@
+// Copyright 2015 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.
+
+// MemoryPressureListener provides static APIs for handling memory pressure on
+// platforms that have such signals (Android, ChromeOS, Mac and Windows). When
+// such signals are received the app will try to discard buffers that aren't
+// deemed essential (individual modules will implement their own policy).
+
+#ifndef COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_LISTENER_H_
+#define COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_LISTENER_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/callback.h"
+
+// ChromeOS and Linux operating systems will be added to this as they are
+// implemented.
+#if defined(OS_WIN)
+#define MEMORY_PRESSURE_IS_POLLING
+#endif
+
+namespace memory_pressure {
+
+// To start listening, create a new instance, passing a callback to a
+// function that takes a MemoryPressureLevel parameter. To stop listening,
+// simply delete the listener object. The implementation guarantees
+// that the callback will always be called on the thread that created
+// the listener.
+//
+// Note that even on the same thread, the callback is not guaranteed to be
+// called synchronously within the system memory pressure broadcast.
+// Please see notes in MemoryPressureLevel enum below: some levels are
+// absolutely critical, and if not enough memory is returned to the system,
+// it'll potentially kill the app, and then later the app will have to be
+// cold-started.
+//
+// Example:
+//
+// void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) {
+// ...
+// }
+//
+// // Start listening.
+// MemoryPressureListener* my_listener =
+// new MemoryPressureListener(base::Bind(&OnMemoryPressure));
+//
+// ...
+//
+// // Stop listening.
+// delete my_listener;
+//
+class MemoryPressureListener {
+ public:
+ // A Java counterpart will be generated for this enum.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
+ enum MemoryPressureLevel {
+ // No problems, there is enough memory to use. This event is not sent via
+ // callback, but the enum is used in other places to find out the current
+ // state of the system.
+ MEMORY_PRESSURE_LEVEL_NONE = -1,
+
+ // Modules are advised to free buffers that are cheap to re-allocate and not
+ // immediately needed.
+ MEMORY_PRESSURE_LEVEL_MODERATE = 0,
+
+ // At this level, modules are advised to free all possible memory. The
+ // alternative is to be killed by the system, which means all memory will
+ // have to be re-created, plus the cost of a cold start.
+ MEMORY_PRESSURE_LEVEL_CRITICAL = 2,
+ };
+
+ typedef base::Callback<void(MemoryPressureLevel)> MemoryPressureCallback;
+
+ explicit MemoryPressureListener(
+ const MemoryPressureCallback& memory_pressure_callback);
+ ~MemoryPressureListener();
+
+ // Intended for use by the platform specific implementation.
+ static void NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level);
+
+ private:
+ void Notify(MemoryPressureLevel memory_pressure_level);
+
+ MemoryPressureCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryPressureListener);
+};
+
+} // namespace memory_pressure
+
+#endif // COMPONENTS_MEMORY_PRESSURE_MEMORY_PRESSURE_LISTENER_H_
diff --git a/components/memory_pressure/test_memory_pressure_calculator.cc b/components/memory_pressure/test_memory_pressure_calculator.cc
new file mode 100644
index 0000000..2e0aa5b
--- /dev/null
+++ b/components/memory_pressure/test_memory_pressure_calculator.cc
@@ -0,0 +1,38 @@
+// Copyright 2015 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 "components/memory_pressure/test_memory_pressure_calculator.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+TestMemoryPressureCalculator::TestMemoryPressureCalculator()
+ : level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), calls_(0) {}
+
+TestMemoryPressureCalculator::MemoryPressureLevel
+TestMemoryPressureCalculator::CalculateCurrentPressureLevel() {
+ ++calls_;
+ return level_;
+}
+
+void TestMemoryPressureCalculator::SetNone() {
+ level_ = MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
+}
+
+void TestMemoryPressureCalculator::SetModerate() {
+ level_ = MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
+}
+
+void TestMemoryPressureCalculator::SetCritical() {
+ level_ = MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
+}
+
+void TestMemoryPressureCalculator::ResetCalls() {
+ calls_ = 0;
+}
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
diff --git a/components/memory_pressure/test_memory_pressure_calculator.h b/components/memory_pressure/test_memory_pressure_calculator.h
new file mode 100644
index 0000000..20ad671
--- /dev/null
+++ b/components/memory_pressure/test_memory_pressure_calculator.h
@@ -0,0 +1,49 @@
+// Copyright 2015 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 COMPONENTS_MEMORY_PRESSURE_TEST_MEMORY_PRESSURE_CALCULATOR_H_
+#define COMPONENTS_MEMORY_PRESSURE_TEST_MEMORY_PRESSURE_CALCULATOR_H_
+
+#include "components/memory_pressure/memory_pressure_calculator.h"
+
+namespace memory_pressure {
+
+#if defined(MEMORY_PRESSURE_IS_POLLING)
+
+// A mock memory pressure calculator for unittesting.
+class TestMemoryPressureCalculator : public MemoryPressureCalculator {
+ public:
+ TestMemoryPressureCalculator();
+ virtual ~TestMemoryPressureCalculator() {}
+
+ // MemoryPressureCalculator implementation.
+ MemoryPressureLevel CalculateCurrentPressureLevel() override;
+
+ // Sets the mock calculator to return no pressure.
+ void SetNone();
+
+ // Sets the mock calculator to return moderate pressure.
+ void SetModerate();
+
+ // Sets the mock calculator to return critical pressure.
+ void SetCritical();
+
+ // Resets the call counter to 'CalculateCurrentPressureLevel'.
+ void ResetCalls();
+
+ // Returns the number of calls to 'CalculateCurrentPressureLevel'.
+ int calls() const { return calls_; }
+
+ private:
+ MemoryPressureLevel level_;
+ int calls_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureCalculator);
+};
+
+#endif // defined(MEMORY_PRESSURE_IS_POLLING)
+
+} // namespace memory_pressure
+
+#endif // COMPONENTS_MEMORY_PRESSURE_TEST_MEMORY_PRESSURE_CALCULATOR_H_