cachelib/allocator/Util.h (90 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 <cstdint>
#include <cstring>
#include <set>
#include <stdexcept>
#include <thread>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#include <folly/futures/Future.h>
#pragma GCC diagnostic pop
#include <cachelib/allocator/KAllocation.h>
#include <cachelib/allocator/memory/MemoryAllocator.h>
#include <cachelib/allocator/memory/Slab.h>
namespace facebook {
namespace cachelib {
namespace util {
// inserts the IOBuf and its chain into the cache by copying the bufffers into
// bunch of parent and child items. The chain can be accessed by calling
// find() on the parent key. If there is already an allocation with the same
// key, it is replaced.
//
// @param cache the cache to make the allocations from
// @param pid the pool id for allocation
// @param key the key for the allocation
// @param bufs the chain of IOBufs to insert into the cache
// @param ttlSecs the ttl for the allocation.
//
// @return true if the chain was successfully inserted inot cache. false if
// there was any failure allocating memory.
template <typename T>
bool insertIOBufInCache(T& cache,
PoolId pid,
typename T::Key key,
const folly::IOBuf& bufs,
uint32_t ttlSecs = 0) {
auto parent = cache.allocate(pid, key, bufs.length(), ttlSecs);
if (!parent) {
return false;
}
std::memcpy(parent->getMemory(), bufs.data(), bufs.length());
if (bufs.isChained()) {
auto itr = bufs.begin();
++itr; // we already processed the first in chain
while (itr != bufs.end()) {
auto alloc = cache.allocateChainedItem(parent, itr->size());
if (!alloc) {
// this will release the parent all its existing children.
return false;
}
// copy data
// TODO (sathya) use the extra space in this allocation to sneak more
// bits from the next IOBuf ?
std::memcpy(alloc->getMemory(), itr->data(), itr->size());
cache.addChainedItem(parent, std::move(alloc));
XDCHECK(parent->hasChainedItem());
++itr;
}
}
cache.insertOrReplace(parent);
return true;
}
// Allocates and inserts an item in the cache without initializing the memory.
// Typically used for tests where we dont have synchronous readers/writers.
//
// @param cache the cache to make the allocations from
// @param poolId the pool id for allocation
// @param key the key for the allocation
// @param size the size of the allocation
// @param ttlSecs Time To Live (second) for the item,
// default with 0 means no expiration time
//
// @return the handle for the item or an invalid handle(nullptr) if the
// allocation/insertion failed.
template <typename T>
typename T::ItemHandle allocateAccessible(T& cache,
PoolId poolId,
typename T::Item::Key key,
uint32_t size,
uint32_t ttlSecs = 0) {
auto allocHandle = cache.allocate(poolId, key, size, ttlSecs);
if (!allocHandle) {
return typename T::ItemHandle{};
}
const auto inserted = cache.insert(allocHandle);
if (!inserted) {
// this will destroy the allocated handle and release it back to the
// allocator.
return typename T::ItemHandle{};
}
return allocHandle;
}
// Convenience method: cast a type with standard layout as a key
template <typename T>
folly::StringPiece castToKey(const T& type) noexcept {
static_assert(std::is_standard_layout<T>::value,
"Key must be standard layout");
return folly::StringPiece{reinterpret_cast<const char*>(&type), sizeof(T)};
}
// Returns item's fragmentation size caused by the bad allocation.
// this should be equal to alloc_size - required_size.
//
// @param item reference of the cache
// @param item reference of the item
// @return the memory fragmentation size of this item.
template <typename T, typename U>
uint32_t getFragmentation(const T& cache, const U& item) {
return cache.getUsableSize(item) - item.getSize();
}
// Check if the given key is valid. If the key is not valid, and is passed to
// other cachelib functions, they might throw exceptions, return null or fail
// with an error
//
// @param key The key to check
// @return true if the key is a valid cachelib key, false otherwise
inline bool isKeyValid(folly::StringPiece key) {
return KAllocation::isKeyValid(key);
}
// Same as isKeyValid() above, but throws a std::invalid_argument error when
// the key is not valid
inline void throwIfKeyInvalid(folly::StringPiece key) {
KAllocation::throwIfKeyInvalid(key);
}
// helper function to generate the allocation sizes for addPool()
inline std::set<uint32_t> generateAllocSizes(
double allocationClassSizeFactor,
uint32_t maxAllocationClassSize = Slab::kSize,
uint32_t minAllocationClassSize = 72,
bool reduceFragmentationInAllocationClass = false) {
return MemoryAllocator::generateAllocSizes(
allocationClassSizeFactor,
maxAllocationClassSize,
minAllocationClassSize,
reduceFragmentationInAllocationClass);
}
} // namespace util
} // namespace cachelib
} // namespace facebook