cachelib/allocator/nvmcache/TombStones.h (74 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/container/F14Map.h>
#include <folly/lang/Align.h>
#include <glog/logging.h>
#include <mutex>
#include <utility>
#include "folly/Range.h"
namespace facebook {
namespace cachelib {
// Utility that helps us track in flight deletes. We maintain a count per key
// and check for presence against the count to resolve multiple concurrent
// deletes for the same key in flight.
class alignas(folly::hardware_destructive_interference_size) TombStones {
public:
class Guard;
// adds an instance of key
// @param key key for the record
// @return a valid Guard representing the tombstone
Guard add(folly::StringPiece key) {
std::lock_guard<std::mutex> l(mutex_);
auto it = keys_.find(key);
if (it == keys_.end()) {
it = keys_.insert(std::make_pair(key.toString(), 0)).first;
}
++it->second;
return Guard(it->first, *this);
}
// checks if there is a key present and returns true if so.
bool isPresent(folly::StringPiece key) {
std::lock_guard<std::mutex> l(mutex_);
return keys_.count(key) != 0;
}
// Guard that wraps around the tombstone record. Removes the key from the
// tombstone records upon destruction. A valid guard can be only created by
// adding to the tombstone record.
class Guard {
public:
Guard() {}
~Guard() {
if (tombstones_) {
tombstones_->remove(key_);
tombstones_ = nullptr;
}
}
// disable copying
Guard(const Guard&) = delete;
Guard& operator=(const Guard&&) = delete;
// allow moving
Guard(Guard&& other) noexcept
: key_{std::move(other.key_)}, tombstones_(other.tombstones_) {
other.tombstones_ = nullptr;
}
Guard& operator=(Guard&& other) noexcept {
if (this != &other) {
this->~Guard();
new (this) Guard(std::move(other));
}
return *this;
}
folly::StringPiece key() const noexcept { return key_; }
explicit operator bool() const noexcept { return tombstones_ != nullptr; }
private:
// only tombstone can create a guard.
friend TombStones;
Guard(folly::StringPiece key, TombStones& t) noexcept
: key_(key), tombstones_(&t) {}
// key for the tombstone
folly::StringPiece key_;
// tombstone record
TombStones* tombstones_{nullptr};
};
private:
// removes an instance of key. if the count drops to 0, we remove the key
void remove(folly::StringPiece key) {
std::lock_guard<std::mutex> l(mutex_);
auto it = keys_.find(key);
if (it == keys_.end() || it->second == 0) {
// this is not supposed to happen if guards are destroyed appropriately
throw std::runtime_error(fmt::format(
"Invalid state. Key: {}. State: {}", key,
it == keys_.end() ? "does not exist" : "exists, but count is 0"));
}
if (--(it->second) == 0) {
keys_.erase(it);
}
}
// mutex protecting the map below
std::mutex mutex_;
folly::F14NodeMap<std::string, uint64_t> keys_;
};
} // namespace cachelib
} // namespace facebook