// 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 #include #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; 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::Leaky g_all_trackers; } // namespace. // static scoped_refptr 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 insert_result = all_trackers->map.insert( std::pair(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