Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2507)

Unified Diff: base/memory/memory_pressure_monitor_mac.cc

Issue 1587273002: [Mac] Collect real-time memory pressure stats, in an energy-efficient way (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix nits. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/memory/memory_pressure_monitor_mac.h ('k') | base/memory/memory_pressure_monitor_mac_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/memory/memory_pressure_monitor_mac.cc
diff --git a/base/memory/memory_pressure_monitor_mac.cc b/base/memory/memory_pressure_monitor_mac.cc
index 391589021bd3cd6988fa10d53cf36535301e3b9e..6261de226c20296c504964a6037c012cb2f2fb32 100644
--- a/base/memory/memory_pressure_monitor_mac.cc
+++ b/base/memory/memory_pressure_monitor_mac.cc
@@ -4,6 +4,8 @@
#include "base/memory/memory_pressure_monitor_mac.h"
+#include <CoreFoundation/CoreFoundation.h>
+
#include <dlfcn.h>
#include <stddef.h>
#include <sys/sysctl.h>
@@ -26,9 +28,9 @@ namespace base {
namespace mac {
MemoryPressureListener::MemoryPressureLevel
-MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressure(
- int mac_memory_pressure) {
- switch (mac_memory_pressure) {
+MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel(
+ int mac_memory_pressure_level) {
+ switch (mac_memory_pressure_level) {
case DISPATCH_MEMORYPRESSURE_NORMAL:
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
case DISPATCH_MEMORYPRESSURE_WARN:
@@ -39,6 +41,13 @@ MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressure(
return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
+void MemoryPressureMonitor::OnRunLoopExit(CFRunLoopObserverRef observer,
+ CFRunLoopActivity activity,
+ void* info) {
+ MemoryPressureMonitor* self = static_cast<MemoryPressureMonitor*>(info);
+ self->UpdatePressureLevelOnRunLoopExit();
+}
+
MemoryPressureMonitor::MemoryPressureMonitor()
: memory_level_event_source_(dispatch_source_create(
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE,
@@ -48,75 +57,138 @@ MemoryPressureMonitor::MemoryPressureMonitor()
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))),
dispatch_callback_(
base::Bind(&MemoryPressureListener::NotifyMemoryPressure)),
- last_statistic_report_(CFAbsoluteTimeGetCurrent()),
+ last_statistic_report_time_(CFAbsoluteTimeGetCurrent()),
last_pressure_level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
- reporting_error_(0) {
- if (memory_level_event_source_.get() != nullptr) {
+ subtick_seconds_(0) {
+ // Attach an event handler to the memory pressure event source.
+ if (memory_level_event_source_.get()) {
dispatch_source_set_event_handler(memory_level_event_source_, ^{
OnMemoryPressureChanged(memory_level_event_source_.get(),
dispatch_callback_);
});
+
+ // Start monitoring the event source.
dispatch_resume(memory_level_event_source_);
}
+
+ // Create a CFRunLoopObserver to check the memory pressure at the end of
+ // every pass through the event loop (modulo kUMATickSize).
+ CFRunLoopObserverContext observer_context = {0, this, NULL, NULL, NULL};
+
+ exit_observer_.reset(
+ CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, true, 0,
+ OnRunLoopExit, &observer_context));
+
+ CFRunLoopRef run_loop = CFRunLoopGetCurrent();
+ CFRunLoopAddObserver(run_loop, exit_observer_, kCFRunLoopCommonModes);
+ CFRunLoopAddObserver(run_loop, exit_observer_,
+ kMessageLoopExclusiveRunLoopMode);
}
MemoryPressureMonitor::~MemoryPressureMonitor() {
- if (memory_level_event_source_.get() != nullptr)
+ // Detach from the run loop.
+ CFRunLoopRef run_loop = CFRunLoopGetCurrent();
+ CFRunLoopRemoveObserver(run_loop, exit_observer_, kCFRunLoopCommonModes);
+ CFRunLoopRemoveObserver(run_loop, exit_observer_,
+ kMessageLoopExclusiveRunLoopMode);
+
+ // Remove the memory pressure event source.
+ if (memory_level_event_source_.get()) {
dispatch_source_cancel(memory_level_event_source_);
+ }
}
-MemoryPressureListener::MemoryPressureLevel
-MemoryPressureMonitor::GetCurrentPressureLevel() {
- int mac_memory_pressure;
+int MemoryPressureMonitor::GetMacMemoryPressureLevel() {
+ // Get the raw memory pressure level from macOS.
+ int mac_memory_pressure_level;
size_t length = sizeof(int);
- sysctlbyname("kern.memorystatus_vm_pressure_level", &mac_memory_pressure,
- &length, nullptr, 0);
- MemoryPressureListener::MemoryPressureLevel memory_pressure_level =
- MemoryPressureLevelForMacMemoryPressure(mac_memory_pressure);
- bool pressure_level_changed = false;
- if (last_pressure_level_ != memory_pressure_level) {
- pressure_level_changed = true;
- }
- SendStatisticsIfNecessary(pressure_level_changed);
- last_pressure_level_ = memory_pressure_level;
- return memory_pressure_level;
-}
+ sysctlbyname("kern.memorystatus_vm_pressure_level",
+ &mac_memory_pressure_level, &length, nullptr, 0);
-void MemoryPressureMonitor::OnMemoryPressureChanged(
- dispatch_source_s* event_source,
- const MemoryPressureMonitor::DispatchCallback& dispatch_callback) {
- int mac_memory_pressure = dispatch_source_get_data(event_source);
- MemoryPressureListener::MemoryPressureLevel memory_pressure_level =
- MemoryPressureLevelForMacMemoryPressure(mac_memory_pressure);
- bool pressure_level_changed = false;
- if (last_pressure_level_ != memory_pressure_level) {
- pressure_level_changed = true;
- }
- SendStatisticsIfNecessary(pressure_level_changed);
- last_pressure_level_ = memory_pressure_level;
- if (memory_pressure_level !=
- MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE)
- dispatch_callback.Run(memory_pressure_level);
+ return mac_memory_pressure_level;
}
-void MemoryPressureMonitor::SendStatisticsIfNecessary(
- bool pressure_level_changed) {
+void MemoryPressureMonitor::UpdatePressureLevel() {
+ // Get the current macOS pressure level and convert to the corresponding
+ // Chrome pressure level.
+ int mac_memory_pressure_level = GetMacMemoryPressureLevel();
+ MemoryPressureListener::MemoryPressureLevel new_pressure_level =
+ MemoryPressureLevelForMacMemoryPressureLevel(mac_memory_pressure_level);
+
+ // Compute the number of "ticks" spent at |last_pressure_level_| (since the
+ // last report sent to UMA).
CFTimeInterval now = CFAbsoluteTimeGetCurrent();
- CFTimeInterval since_last_report = now - last_statistic_report_;
- last_statistic_report_ = now;
+ CFTimeInterval time_since_last_report = now - last_statistic_report_time_;
+ last_statistic_report_time_ = now;
- double accumulated_time = since_last_report + reporting_error_;
+ double accumulated_time = time_since_last_report + subtick_seconds_;
int ticks_to_report = static_cast<int>(accumulated_time / kUMATickSize);
- reporting_error_ = std::fmod(accumulated_time, kUMATickSize);
+ // Save for later the seconds that didn't make it into a full tick.
+ subtick_seconds_ = std::fmod(accumulated_time, kUMATickSize);
- // Round up on change to ensure we capture it
+ // Round the tick count up on a pressure level change to ensure we capture it.
+ bool pressure_level_changed = (new_pressure_level != last_pressure_level_);
if (pressure_level_changed && ticks_to_report < 1) {
ticks_to_report = 1;
- reporting_error_ = 0;
+ subtick_seconds_ = 0;
}
- if (ticks_to_report >= 1)
+ // Send elapsed ticks to UMA.
+ if (ticks_to_report >= 1) {
RecordMemoryPressure(last_pressure_level_, ticks_to_report);
+ }
+
+ // Save the now-current memory pressure level.
+ last_pressure_level_ = new_pressure_level;
+}
+
+void MemoryPressureMonitor::UpdatePressureLevelOnRunLoopExit() {
+ // Wait until it's time to check the pressure level.
+ CFTimeInterval now = CFAbsoluteTimeGetCurrent();
+ if (now >= next_run_loop_update_time_) {
+ UpdatePressureLevel();
+
+ // Update again in kUMATickSize seconds. We can update at any frequency,
+ // but because we're only checking memory pressure levels for UMA there's
+ // no need to update more frequently than we're keeping statistics on.
+ next_run_loop_update_time_ = now + kUMATickSize - subtick_seconds_;
+ }
+}
+
+// Static.
+int MemoryPressureMonitor::GetSecondsPerUMATick() {
+ return kUMATickSize;
+}
+
+MemoryPressureListener::MemoryPressureLevel
+MemoryPressureMonitor::GetCurrentPressureLevel() {
+ UpdatePressureLevel();
+ return last_pressure_level_;
+}
+
+void MemoryPressureMonitor::OnMemoryPressureChanged(
+ dispatch_source_s* event_source,
+ const MemoryPressureMonitor::DispatchCallback& dispatch_callback) {
+ // Get the Chrome-equvialent memory pressure level.
+ int mac_memory_pressure_level = dispatch_source_get_data(event_source);
+ MemoryPressureListener::MemoryPressureLevel memory_pressure_level =
+ MemoryPressureLevelForMacMemoryPressureLevel(mac_memory_pressure_level);
+
+ // Run the callback that's waiting on memory pressure change notifications.
+ // Note that we don't bother with updating |last_pressure_level_| or
+ // causing memory pressure stats to be sent to UMA. Memory pressure change
+ // notifications are delayed on the Mac, so the current actual memory pressure
+ // level may be different than the incoming pressure level from the event.
+ //
+ // In general we don't want to take action (such as freeing memory) on
+ // memory pressure change events, but that's how the current system is
+ // designed. Given that it's incorrect to act on either stale or current
+ // pressure level info, it's not clear which level is better to send. For
+ // now stick with how it's been implemented to date, which is to send the
+ // stale value.
+ if (memory_pressure_level !=
+ MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE)
+ dispatch_callback.Run(memory_pressure_level);
}
void MemoryPressureMonitor::SetDispatchCallback(
« no previous file with comments | « base/memory/memory_pressure_monitor_mac.h ('k') | base/memory/memory_pressure_monitor_mac_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698