| // Copyright 2018 The Fuchsia 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 "peridot/bin/ledger/app/page_eviction_policies.h" |
| |
| #include "peridot/bin/ledger/coroutine/coroutine_manager.h" |
| |
| namespace ledger { |
| namespace { |
| |
| // Computes the list of PageInfo for all pages that are not currently open, |
| // ordered by the timestamp of their last usage, in ascending order. |
| Status GetPagesByTimestamp( |
| std::unique_ptr<storage::Iterator<const PageInfo>> pages_it, |
| std::vector<PageInfo>* sorted_pages) { |
| std::vector<PageInfo> pages; |
| while (pages_it->Valid()) { |
| // Sort out pages that are currently in use, i.e. those for which timestamp |
| // is |PageInfo::kOpenedPageTimestamp|. |
| if ((*pages_it)->timestamp != PageInfo::kOpenedPageTimestamp) { |
| pages.push_back(**pages_it); |
| } |
| pages_it->Next(); |
| } |
| |
| // Order pages by the last used timestamp. |
| std::sort( |
| pages.begin(), pages.end(), |
| [](const PageInfo& info1, const PageInfo& info2) { |
| return std::tie(info1.timestamp, info1.ledger_name, info1.page_id) < |
| std::tie(info2.timestamp, info2.ledger_name, info2.page_id); |
| }); |
| |
| sorted_pages->swap(pages); |
| return Status::OK; |
| } |
| |
| class LeastRecentlyUsedPageEvictionPolicy : public PageEvictionPolicy { |
| public: |
| LeastRecentlyUsedPageEvictionPolicy( |
| coroutine::CoroutineService* coroutine_service, |
| PageEvictionDelegate* delegate); |
| |
| void SelectAndEvict(std::unique_ptr<storage::Iterator<const PageInfo>> pages, |
| fit::function<void(Status)> callback) override; |
| |
| private: |
| PageEvictionDelegate* delegate_; |
| coroutine::CoroutineManager coroutine_manager_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(LeastRecentlyUsedPageEvictionPolicy); |
| }; |
| |
| LeastRecentlyUsedPageEvictionPolicy::LeastRecentlyUsedPageEvictionPolicy( |
| coroutine::CoroutineService* coroutine_service, |
| PageEvictionDelegate* delegate) |
| : delegate_(delegate), coroutine_manager_(coroutine_service) {} |
| |
| void LeastRecentlyUsedPageEvictionPolicy::SelectAndEvict( |
| std::unique_ptr<storage::Iterator<const PageInfo>> pages_it, |
| fit::function<void(Status)> callback) { |
| coroutine_manager_.StartCoroutine( |
| std::move(callback), [this, pages_it = std::move(pages_it)]( |
| coroutine::CoroutineHandler* handler, |
| fit::function<void(Status)> callback) mutable { |
| std::vector<PageInfo> pages; |
| Status status = GetPagesByTimestamp(std::move(pages_it), &pages); |
| if (status != Status::OK) { |
| callback(status); |
| return; |
| } |
| for (const auto& page_info : pages) { |
| PageWasEvicted was_evicted; |
| auto sync_call_status = coroutine::SyncCall( |
| handler, |
| [this, ledger_name = std::move(page_info.ledger_name), |
| page_id = std::move(page_info.page_id)](auto callback) { |
| delegate_->TryEvictPage(ledger_name, page_id, |
| PageEvictionCondition::IF_POSSIBLE, |
| std::move(callback)); |
| }, |
| &status, &was_evicted); |
| if (sync_call_status == coroutine::ContinuationStatus::INTERRUPTED) { |
| callback(Status::INTERNAL_ERROR); |
| return; |
| } |
| if (status != Status::OK || was_evicted) { |
| callback(status); |
| return; |
| } |
| } |
| callback(Status::OK); |
| }); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<PageEvictionPolicy> NewLeastRecentyUsedPolicy( |
| coroutine::CoroutineService* coroutine_service, |
| PageEvictionDelegate* delegate) { |
| return std::make_unique<LeastRecentlyUsedPageEvictionPolicy>( |
| coroutine_service, delegate); |
| } |
| |
| } // namespace ledger |