| 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(
|
|
|