blob: 9ad8b8aac560c93db412954d5f9cd371f6463f4d [file] [log] [blame]
// Copyright 2014 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 "content/browser/indexed_db/indexed_db_active_blob_registry.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/stl_util.h"
#include "base/task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "content/browser/indexed_db/indexed_db_factory.h"
#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
namespace content {
IndexedDBActiveBlobRegistry::IndexedDBActiveBlobRegistry(
ReportOutstandingBlobsCallback report_outstanding_blobs,
ReportUnusedBlobCallback report_unused_blob)
: report_outstanding_blobs_(std::move(report_outstanding_blobs)),
report_unused_blob_(std::move(report_unused_blob)) {}
IndexedDBActiveBlobRegistry::~IndexedDBActiveBlobRegistry() {}
bool IndexedDBActiveBlobRegistry::MarkDeletedCheckIfUsed(int64_t database_id,
int64_t blob_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
const auto& db_pair = use_tracker_.find(database_id);
if (db_pair == use_tracker_.end())
return false;
if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
deleted_dbs_.insert(database_id);
return true;
}
SingleDBMap& single_db = db_pair->second;
auto iter = single_db.find(blob_key);
if (iter == single_db.end())
return false;
iter->second = BlobState::kDeleted;
return true;
}
IndexedDBBlobInfo::ReleaseCallback
IndexedDBActiveBlobRegistry::GetFinalReleaseCallback(int64_t database_id,
int64_t blob_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::BindRepeating(
&IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe,
base::SequencedTaskRunnerHandle::Get(), weak_factory_.GetWeakPtr(),
database_id, blob_key);
}
base::RepeatingClosure IndexedDBActiveBlobRegistry::GetAddBlobRefCallback(
int64_t database_id,
int64_t blob_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::BindRepeating(&IndexedDBActiveBlobRegistry::AddBlobRef,
weak_factory_.GetWeakPtr(), database_id, blob_key);
}
void IndexedDBActiveBlobRegistry::ForceShutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
weak_factory_.InvalidateWeakPtrs();
use_tracker_.clear();
report_outstanding_blobs_.Reset();
report_unused_blob_.Reset();
}
void IndexedDBActiveBlobRegistry::AddBlobRef(int64_t database_id,
int64_t blob_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(report_outstanding_blobs_);
DCHECK(report_unused_blob_);
DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
DCHECK(!base::Contains(deleted_dbs_, database_id));
bool outstanding_blobs_in_backing_store = !use_tracker_.empty();
SingleDBMap& blobs_in_db = use_tracker_[database_id];
auto iter = blobs_in_db.find(blob_key);
if (iter == blobs_in_db.end()) {
blobs_in_db[blob_key] = BlobState::kAlive;
if (!outstanding_blobs_in_backing_store)
report_outstanding_blobs_.Run(true);
} else {
DCHECK(outstanding_blobs_in_backing_store);
// You can't add a reference once it's been deleted.
DCHECK(iter->second == BlobState::kAlive);
}
}
void IndexedDBActiveBlobRegistry::ReleaseBlobRef(int64_t database_id,
int64_t blob_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(report_outstanding_blobs_);
DCHECK(report_unused_blob_);
DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
const auto& db_pair = use_tracker_.find(database_id);
if (db_pair == use_tracker_.end()) {
NOTREACHED();
return;
}
SingleDBMap& blobs_in_db = db_pair->second;
auto blob_in_db_it = blobs_in_db.find(blob_key);
if (blob_in_db_it == blobs_in_db.end()) {
NOTREACHED();
return;
}
bool delete_blob_in_backend = false;
const auto& deleted_database_it = deleted_dbs_.find(database_id);
bool db_marked_for_deletion = deleted_database_it != deleted_dbs_.end();
// Don't bother deleting the file if we're going to delete its whole
// database directory soon.
delete_blob_in_backend =
blob_in_db_it->second == BlobState::kDeleted && !db_marked_for_deletion;
blobs_in_db.erase(blob_in_db_it);
if (blobs_in_db.empty()) {
use_tracker_.erase(db_pair);
if (db_marked_for_deletion) {
delete_blob_in_backend = true;
blob_key = DatabaseMetaDataKey::kAllBlobsKey;
deleted_dbs_.erase(deleted_database_it);
}
}
if (delete_blob_in_backend)
report_unused_blob_.Run(database_id, blob_key);
if (use_tracker_.empty())
report_outstanding_blobs_.Run(false);
}
void IndexedDBActiveBlobRegistry::ReleaseBlobRefThreadSafe(
scoped_refptr<base::TaskRunner> task_runner,
base::WeakPtr<IndexedDBActiveBlobRegistry> weak_ptr,
int64_t database_id,
int64_t blob_key,
const base::FilePath& unused) {
task_runner->PostTask(
FROM_HERE, base::BindOnce(&IndexedDBActiveBlobRegistry::ReleaseBlobRef,
std::move(weak_ptr), database_id, blob_key));
}
} // namespace content