| // Copyright (c) 2017 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. | |
| // Internal helper used to sequence cleanup and reuse of cache directories | |
| // among different objects. | |
| #include "net/disk_cache/backend_cleanup_tracker.h" | |
| #include <unordered_map> | |
| #include <utility> | |
| #include "base/callback.h" | |
| #include "base/files/file_path.h" | |
| #include "base/lazy_instance.h" | |
| #include "base/memory/ref_counted.h" | |
| #include "base/sequenced_task_runner.h" | |
| #include "base/synchronization/lock.h" | |
| #include "base/task/post_task.h" | |
| #include "base/threading/sequenced_task_runner_handle.h" | |
| namespace disk_cache { | |
| namespace { | |
| using TrackerMap = std::unordered_map<base::FilePath, BackendCleanupTracker*>; | |
| struct AllBackendCleanupTrackers { | |
| TrackerMap map; | |
| // Since clients can potentially call CreateCacheBackend from multiple | |
| // threads, we need to lock the map keeping track of cleanup trackers | |
| // for these backends. Our overall strategy is to have TryCreate | |
| // acts as an arbitrator --- whatever thread grabs one, gets to operate | |
| // on the tracker freely until it gets destroyed. | |
| base::Lock lock; | |
| }; | |
| static base::LazyInstance<AllBackendCleanupTrackers>::Leaky g_all_trackers; | |
| } // namespace. | |
| // static | |
| scoped_refptr<BackendCleanupTracker> BackendCleanupTracker::TryCreate( | |
| const base::FilePath& path, | |
| base::OnceClosure retry_closure) { | |
| AllBackendCleanupTrackers* all_trackers = g_all_trackers.Pointer(); | |
| base::AutoLock lock(all_trackers->lock); | |
| std::pair<TrackerMap::iterator, bool> insert_result = | |
| all_trackers->map.insert( | |
| std::pair<base::FilePath, BackendCleanupTracker*>(path, nullptr)); | |
| if (insert_result.second) { | |
| insert_result.first->second = new BackendCleanupTracker(path); | |
| return insert_result.first->second; | |
| } else { | |
| insert_result.first->second->AddPostCleanupCallbackImpl( | |
| std::move(retry_closure)); | |
| return nullptr; | |
| } | |
| } | |
| void BackendCleanupTracker::AddPostCleanupCallback(base::OnceClosure cb) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(seq_checker_); | |
| // Despite the sequencing requirement we need to grab the table lock since | |
| // this may otherwise race against TryMakeContext. | |
| base::AutoLock lock(g_all_trackers.Get().lock); | |
| AddPostCleanupCallbackImpl(std::move(cb)); | |
| } | |
| void BackendCleanupTracker::AddPostCleanupCallbackImpl(base::OnceClosure cb) { | |
| post_cleanup_cbs_.push_back( | |
| std::make_pair(base::SequencedTaskRunnerHandle::Get(), std::move(cb))); | |
| } | |
| BackendCleanupTracker::BackendCleanupTracker(const base::FilePath& path) | |
| : path_(path) {} | |
| BackendCleanupTracker::~BackendCleanupTracker() { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(seq_checker_); | |
| { | |
| AllBackendCleanupTrackers* all_trackers = g_all_trackers.Pointer(); | |
| base::AutoLock lock(all_trackers->lock); | |
| int rv = all_trackers->map.erase(path_); | |
| DCHECK_EQ(1, rv); | |
| } | |
| while (!post_cleanup_cbs_.empty()) { | |
| post_cleanup_cbs_.back().first->PostTask( | |
| FROM_HERE, std::move(post_cleanup_cbs_.back().second)); | |
| post_cleanup_cbs_.pop_back(); | |
| } | |
| } | |
| } // namespace disk_cache |