blob: 48dc85773916e4fbec1dfddad339f0caaf00ffd6 [file] [log] [blame]
// 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 "platform/MemoryCoordinator.h"
#include "base/sys_info.h"
#include "build/build_config.h"
#include "platform/CrossThreadFunctional.h"
#include "platform/WebTaskRunner.h"
#include "platform/fonts/FontGlobalContext.h"
#include "platform/graphics/ImageDecodingStore.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/wtf/allocator/Partitions.h"
#include "public/platform/WebThread.h"
#include "public/web/WebKit.h"
#include "third_party/WebKit/common/device_memory/approximated_device_memory.h"
#if defined(OS_ANDROID)
#include "base/android/sys_utils.h"
#endif
namespace blink {
// Wrapper function defined in WebKit.h
void DecommitFreeableMemory() {
WTF::Partitions::DecommitFreeableMemory();
}
// static
bool MemoryCoordinator::is_low_end_device_ = false;
// static
bool MemoryCoordinator::IsLowEndDevice() {
return is_low_end_device_;
}
// static
bool MemoryCoordinator::IsCurrentlyLowMemory() {
#if defined(OS_ANDROID)
return base::android::SysUtils::IsCurrentlyLowMemory();
#else
return false;
#endif
}
// static
void MemoryCoordinator::Initialize() {
is_low_end_device_ = ::base::SysInfo::IsLowEndDevice();
ApproximatedDeviceMemory::Initialize();
}
// static
void MemoryCoordinator::SetIsLowEndDeviceForTesting(bool is_low_end_device) {
is_low_end_device_ = is_low_end_device;
}
// static
MemoryCoordinator& MemoryCoordinator::Instance() {
DEFINE_STATIC_LOCAL(Persistent<MemoryCoordinator>, external,
(new MemoryCoordinator));
DCHECK(IsMainThread());
return *external.Get();
}
void MemoryCoordinator::RegisterThread(WebThread* thread) {
MemoryCoordinator::Instance().web_threads_.insert(thread);
}
void MemoryCoordinator::UnregisterThread(WebThread* thread) {
MemoryCoordinator::Instance().web_threads_.erase(thread);
}
MemoryCoordinator::MemoryCoordinator() = default;
void MemoryCoordinator::RegisterClient(MemoryCoordinatorClient* client) {
DCHECK(IsMainThread());
DCHECK(client);
DCHECK(!clients_.Contains(client));
clients_.insert(client);
}
void MemoryCoordinator::UnregisterClient(MemoryCoordinatorClient* client) {
DCHECK(IsMainThread());
clients_.erase(client);
}
void MemoryCoordinator::OnMemoryPressure(WebMemoryPressureLevel level) {
TRACE_EVENT0("blink", "MemoryCoordinator::onMemoryPressure");
for (auto& client : clients_)
client->OnMemoryPressure(level);
if (level == kWebMemoryPressureLevelCritical)
ClearMemory();
WTF::Partitions::DecommitFreeableMemory();
}
void MemoryCoordinator::OnMemoryStateChange(MemoryState state) {
for (auto& client : clients_)
client->OnMemoryStateChange(state);
}
void MemoryCoordinator::OnPurgeMemory() {
for (auto& client : clients_)
client->OnPurgeMemory();
// Don't call clearMemory() because font cache invalidation always causes full
// layout. This increases tab switching cost significantly (e.g.
// en.wikipedia.org/wiki/Wikipedia). So we should not invalidate the font
// cache in purge+throttle.
ImageDecodingStore::Instance().Clear();
WTF::Partitions::DecommitFreeableMemory();
// Thread-specific data never issues a layout, so we are safe here.
for (auto thread : web_threads_) {
if (!thread->GetWebTaskRunner())
continue;
PostCrossThreadTask(
*thread->GetWebTaskRunner(), FROM_HERE,
CrossThreadBind(MemoryCoordinator::ClearThreadSpecificMemory));
}
}
void MemoryCoordinator::ClearMemory() {
// Clear the image cache.
// TODO(tasak|bashi): Make ImageDecodingStore and FontCache be
// MemoryCoordinatorClients rather than clearing caches here.
ImageDecodingStore::Instance().Clear();
FontGlobalContext::ClearMemory();
}
void MemoryCoordinator::ClearThreadSpecificMemory() {
FontGlobalContext::ClearMemory();
}
void MemoryCoordinator::Trace(blink::Visitor* visitor) {
visitor->Trace(clients_);
}
} // namespace blink