blob: edbc4e7ea823ef67d008b5deb26b64560904ea26 [file] [log] [blame]
// Copyright 2019 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 "src/ledger/bin/storage/impl/object_identifier_factory_impl.h"
#include <sstream>
#include <string>
#include "src/ledger/bin/storage/impl/object_digest.h"
#include "src/ledger/bin/storage/impl/object_identifier_encoding.h"
#include "src/ledger/bin/storage/public/types.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace storage {
namespace {
// Converts a map of ObjectDigest counts to a string listing them.
std::string TokenCountsToString(
const std::map<ObjectDigest, std::weak_ptr<ObjectIdentifier::Token>>& tokens) {
std::ostringstream stream;
for (const auto& token : tokens) {
stream << "\n" << token.first << " " << token.second.use_count();
}
return stream.str();
}
} // namespace
// Token implementation that cleans up its entry in the the token map upon destruction.
class ObjectIdentifierFactoryImpl::TokenImpl : public ObjectIdentifier::Token {
public:
explicit TokenImpl(
fxl::WeakPtr<ObjectIdentifierFactoryImpl> tracker,
std::map<ObjectDigest, std::weak_ptr<ObjectIdentifier::Token>>::iterator map_entry)
: tracker_(std::move(tracker)), map_entry_(map_entry) {}
~TokenImpl() override {
if (!tracker_) {
FXL_VLOG(1) << "ObjectIdentifier: stop tracking an object after the factory was destructed";
return;
}
ObjectDigest object_digest = map_entry_->first;
FXL_VLOG(2) << "ObjectIdentifier: stop tracking " << object_digest;
FXL_DCHECK(tracker_->thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(tracker_->dispatcher_checker_.IsCreationDispatcherCurrent());
FXL_DCHECK(map_entry_->second.expired());
tracker_->tokens_.erase(map_entry_);
if (tracker_->on_untracked_object_) {
tracker_->on_untracked_object_(object_digest);
}
}
ObjectIdentifierFactory* factory() const override { return tracker_.get(); }
private:
fxl::WeakPtr<ObjectIdentifierFactoryImpl> tracker_;
std::map<ObjectDigest, std::weak_ptr<ObjectIdentifier::Token>>::iterator map_entry_;
};
ObjectIdentifierFactoryImpl::ObjectIdentifierFactoryImpl() : weak_factory_(this) {}
ObjectIdentifierFactoryImpl::~ObjectIdentifierFactoryImpl() {
if (!tokens_.empty()) {
FXL_VLOG(1) << "Destructing ObjectIdentifierFactory with remaining live tokens: "
<< TokenCountsToString(tokens_);
}
}
std::shared_ptr<ObjectIdentifier::Token> ObjectIdentifierFactoryImpl::GetToken(
ObjectDigest digest) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(dispatcher_checker_.IsCreationDispatcherCurrent());
auto [it, created] = tokens_.try_emplace(std::move(digest));
if (!created) {
FXL_DCHECK(!it->second.expired());
return it->second.lock();
}
FXL_VLOG(2) << "ObjectIdentifier: start tracking " << it->first;
auto token = std::make_shared<TokenImpl>(weak_factory_.GetWeakPtr(), it);
it->second = token;
FXL_DCHECK(it->second.use_count() == 1);
return token;
}
long ObjectIdentifierFactoryImpl::count(const ObjectDigest& digest) const {
auto it = tokens_.find(digest);
if (it == tokens_.end()) {
return 0;
}
return it->second.use_count();
}
int ObjectIdentifierFactoryImpl::size() const { return tokens_.size(); }
void ObjectIdentifierFactoryImpl::SetUntrackedCallback(
fit::function<void(const ObjectDigest&)> callback) {
on_untracked_object_ = std::move(callback);
}
bool ObjectIdentifierFactoryImpl::TrackDeletion(const ObjectDigest& object_digest) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(dispatcher_checker_.IsCreationDispatcherCurrent());
if (tokens_.find(object_digest) != tokens_.end()) {
// The object is tracked currently.
return false;
}
if (!deletion_aborted_.emplace(object_digest, false).second) {
// The object is already pending deletion.
return false;
}
FXL_VLOG(1) << "Start deletion " << object_digest;
return true;
}
void ObjectIdentifierFactoryImpl::AbortDeletion(const ObjectDigest& object_digest) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(dispatcher_checker_.IsCreationDispatcherCurrent());
auto it = deletion_aborted_.find(object_digest);
if (it == deletion_aborted_.end()) {
// The object is not pending deletion.
return;
}
FXL_VLOG(1) << "Abort deletion " << object_digest;
it->second = true;
}
bool ObjectIdentifierFactoryImpl::UntrackDeletion(const ObjectDigest& object_digest) {
FXL_DCHECK(thread_checker_.IsCreationThreadCurrent());
FXL_DCHECK(dispatcher_checker_.IsCreationDispatcherCurrent());
auto it = deletion_aborted_.find(object_digest);
if (it == deletion_aborted_.end()) {
// The object is not pending deletion.
FXL_NOTREACHED() << "Unbalanced calls to start and abort deletion of object " << object_digest;
return false;
}
FXL_VLOG(1) << "Complete deletion " << object_digest;
const bool deletion_aborted = it->second;
deletion_aborted_.erase(it);
return !deletion_aborted;
}
ObjectIdentifier ObjectIdentifierFactoryImpl::MakeObjectIdentifier(uint32_t key_index,
ObjectDigest object_digest) {
// Creating an object identifier automatically aborts any pending deletion on the object.
AbortDeletion(object_digest);
auto token = GetToken(object_digest);
return ObjectIdentifier(key_index, std::move(object_digest), std::move(token));
}
bool ObjectIdentifierFactoryImpl::MakeObjectIdentifierFromStorageBytes(
convert::ExtendedStringView storage_bytes, ObjectIdentifier* object_identifier) {
if (!DecodeObjectIdentifier(convert::ToStringView(storage_bytes), this, object_identifier)) {
return false;
}
return IsDigestValid(object_identifier->object_digest());
}
std::string ObjectIdentifierFactoryImpl::ObjectIdentifierToStorageBytes(
const ObjectIdentifier& identifier) {
return EncodeObjectIdentifier(identifier);
}
} // namespace storage