Add method for suppressing MemoryPressureListener notifications

This patch adds a new static method to MemoryPressureListener which can
be used to start/stop suppressing memory pressure notifications:

  // Start suppressing memory pressure notifications.
  MemoryPressureListener::SetNotificationsSuppressed(true);

  // Stop suppressing memory pressure notifications.
  MemoryPressureListener::SetNotificationsSuppressed(false);

This patch represents the first step towards implementing a DevTools API
for suppressing and simulating memory pressure signals in Chrome. The
main use case for this feature is to enforce consistent conditions
across memory measurements. See https://goo.gl/cZFdH3 for more details.

BUG=516776

Review URL: https://codereview.chromium.org/1312163003

Cr-Commit-Position: refs/heads/master@{#347622}


CrOS-Libchrome-Original-Commit: da4982addfa8b6a5e25b794565c6c7158c1ec8cc
diff --git a/base/base.gyp b/base/base.gyp
index db52218..09ab7de 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -519,6 +519,7 @@
         'memory/aligned_memory_unittest.cc',
         'memory/discardable_shared_memory_unittest.cc',
         'memory/linked_ptr_unittest.cc',
+        'memory/memory_pressure_listener_unittest.cc',
         'memory/memory_pressure_monitor_chromeos_unittest.cc',
         'memory/memory_pressure_monitor_mac_unittest.cc',
         'memory/memory_pressure_monitor_win_unittest.cc',
diff --git a/base/memory/memory_pressure_listener.cc b/base/memory/memory_pressure_listener.cc
index 2a1be74..8071d37 100644
--- a/base/memory/memory_pressure_listener.cc
+++ b/base/memory/memory_pressure_listener.cc
@@ -32,6 +32,10 @@
     ObserverListThreadSafe<MemoryPressureListener>,
     LeakyLazyObserverListTraits> g_observers = LAZY_INSTANCE_INITIALIZER;
 
+// All memory pressure notifications within this process will be suppressed if
+// this variable is set to 1.
+subtle::Atomic32 g_notifications_suppressed = 0;
+
 }  // namespace
 
 MemoryPressureListener::MemoryPressureListener(
@@ -54,8 +58,20 @@
   DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
   TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure",
       "level", memory_pressure_level);
+  if (AreNotificationsSuppressed())
+    return;
   g_observers.Get().Notify(FROM_HERE, &MemoryPressureListener::Notify,
                            memory_pressure_level);
 }
 
+// static
+bool MemoryPressureListener::AreNotificationsSuppressed() {
+  return subtle::Acquire_Load(&g_notifications_suppressed) == 1;
+}
+
+// static
+void MemoryPressureListener::SetNotificationsSuppressed(bool suppress) {
+  subtle::Release_Store(&g_notifications_suppressed, suppress ? 1 : 0);
+}
+
 }  // namespace base
diff --git a/base/memory/memory_pressure_listener.h b/base/memory/memory_pressure_listener.h
index 6adaeee..83d47d7 100644
--- a/base/memory/memory_pressure_listener.h
+++ b/base/memory/memory_pressure_listener.h
@@ -72,6 +72,12 @@
   // Intended for use by the platform specific implementation.
   static void NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level);
 
+  // These methods should not be used anywhere else but in memory measurement
+  // code, where they are intended to maintain stable conditions across
+  // measurements.
+  static bool AreNotificationsSuppressed();
+  static void SetNotificationsSuppressed(bool suppressed);
+
  private:
   void Notify(MemoryPressureLevel memory_pressure_level);
 
diff --git a/base/memory/memory_pressure_listener_unittest.cc b/base/memory/memory_pressure_listener_unittest.cc
new file mode 100644
index 0000000..1bcab2f
--- /dev/null
+++ b/base/memory/memory_pressure_listener_unittest.cc
@@ -0,0 +1,52 @@
+// 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 "base/memory/memory_pressure_listener.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+using MockCallback =
+    testing::MockFunction<void(MemoryPressureListener::MemoryPressureLevel)>;
+
+TEST(MemoryPressureListenerTest, NotifyMemoryPressure) {
+  MessageLoopForUI message_loop;
+  MockCallback callback;
+  scoped_ptr<MemoryPressureListener> listener(new MemoryPressureListener(
+      Bind(&MockCallback::Call, Unretained(&callback))));
+
+  // Memory pressure notifications are not suppressed by default.
+  EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed());
+  EXPECT_CALL(callback,
+              Call(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE))
+      .Times(1);
+  MemoryPressureListener::NotifyMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  message_loop.RunUntilIdle();
+
+  // Enable suppressing memory pressure notifications.
+  MemoryPressureListener::SetNotificationsSuppressed(true);
+  EXPECT_TRUE(MemoryPressureListener::AreNotificationsSuppressed());
+  EXPECT_CALL(callback, Call(testing::_)).Times(0);
+  MemoryPressureListener::NotifyMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  message_loop.RunUntilIdle();
+
+  // Disable suppressing memory pressure notifications.
+  MemoryPressureListener::SetNotificationsSuppressed(false);
+  EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed());
+  EXPECT_CALL(callback,
+              Call(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL))
+      .Times(1);
+  MemoryPressureListener::NotifyMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
+  message_loop.RunUntilIdle();
+
+  listener.reset();
+}
+
+}  // namespace base