| // Copyright 2018 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 "chromecast/browser/lru_renderer_cache.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "chromecast/browser/renderer_prelauncher.h" |
| #include "content/public/browser/site_instance.h" |
| |
| namespace chromecast { |
| |
| LRURendererCache::LRURendererCache( |
| content::BrowserContext* browser_context, |
| size_t max_renderers) |
| : browser_context_(browser_context), |
| max_renderers_(max_renderers), |
| in_use_count_(0), |
| weak_factory_(this) { |
| DCHECK(browser_context_); |
| memory_pressure_listener_ = |
| std::make_unique<base::MemoryPressureListener>(base::BindRepeating( |
| &LRURendererCache::OnMemoryPressure, weak_factory_.GetWeakPtr())); |
| } |
| |
| LRURendererCache::~LRURendererCache() = default; |
| |
| std::unique_ptr<RendererPrelauncher> LRURendererCache::TakeRendererPrelauncher( |
| const GURL& page_url) { |
| in_use_count_++; |
| std::unique_ptr<RendererPrelauncher> search; |
| for (auto it = cache_.begin(); it != cache_.end(); ++it) { |
| if ((*it)->IsForURL(page_url)) { |
| // Found pre-launched renderer for this site, return it. |
| search = std::move(*it); |
| cache_.erase(it); |
| LOG(INFO) << "Cache hit for pre-launched SiteInstance: " |
| << page_url.spec(); |
| break; |
| } |
| } |
| |
| if (!search) { |
| // TODO(jlevasseur): Submit metric? |
| LOG(WARNING) << "Cache miss for pre-launched SiteInstance: " |
| << page_url.spec(); |
| } |
| |
| EvictCache(); |
| return search; |
| } |
| |
| void LRURendererCache::EvictCache() { |
| // Evict least-recently-used renderers so that the total number of renderers |
| // doesn't exceed |max_renderers_|, or until the cache is empty. |
| while (!cache_.empty() && in_use_count_ + cache_.size() > max_renderers_) { |
| LOG(INFO) << "Evicting pre-launched SiteInstance: " << cache_.back()->url(); |
| cache_.pop_back(); |
| } |
| } |
| |
| void LRURendererCache::ReleaseRendererPrelauncher(const GURL& page_url) { |
| DCHECK(in_use_count_ > 0); |
| in_use_count_--; |
| if (in_use_count_ >= max_renderers_) { |
| DCHECK(cache_.empty()); |
| // We don't have room to maintain a cache, so don't prelaunch this site even |
| // though it's the most recently used. |
| return; |
| } |
| if (!page_url.is_valid()) { |
| // Can't cache an invalid site. |
| return; |
| } |
| // We have room to maintain a non-empty cache, so we can pre-launch the |
| // renderer process for the next site. We post this as a task to ensure that |
| // the prior site (which is in the process of being released) has completed |
| // destruction; otherwise, its renderer process will overlap with the next |
| // pre-launched process, temporarily exceeding |max_renderers_|. |
| base::SequencedTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&LRURendererCache::StartNextPrelauncher, |
| weak_factory_.GetWeakPtr(), page_url)); |
| } |
| |
| void LRURendererCache::StartNextPrelauncher(const GURL& page_url) { |
| if (in_use_count_ >= max_renderers_) { |
| DCHECK(cache_.empty()); |
| // The maximum number of renderers is already in use, so the cache must |
| // remain empty. |
| return; |
| } |
| DLOG(INFO) << "Pre-launching a renderer for: " << page_url.spec(); |
| if (factory_for_testing_) { |
| cache_.push_front(factory_for_testing_->Create(browser_context_, page_url)); |
| } else { |
| cache_.push_front( |
| std::make_unique<RendererPrelauncher>(browser_context_, page_url)); |
| } |
| // Evict the cache before prelaunching. |
| EvictCache(); |
| cache_.front()->Prelaunch(); |
| } |
| |
| void LRURendererCache::OnMemoryPressure( |
| base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
| if (memory_pressure_level == |
| base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
| DLOG(INFO) << "Dropping prelauncher cache due to memory pressure."; |
| cache_.clear(); |
| } |
| } |
| |
| void LRURendererCache::SetFactoryForTesting( |
| RendererPrelauncherFactory* factory) { |
| DCHECK(factory); |
| factory_for_testing_ = factory; |
| } |
| |
| } // namespace chromecast |