in cachelib/allocator/CacheAllocator-inl.h [1883:2015]
folly::IOBuf CacheAllocator<CacheTrait>::convertToIOBufT(Handle& handle) {
if (!handle) {
throw std::invalid_argument("null item handle for converting to IOBUf");
}
Item* item = handle.getInternal();
const uint32_t dataOffset = item->getOffsetForMemory();
using ConvertChainedItem = std::function<std::unique_ptr<folly::IOBuf>(
Item * item, ChainedItem & chainedItem)>;
folly::IOBuf iobuf;
ConvertChainedItem converter;
// based on current refcount and threshold from config
// determine to use a new ItemHandle for each chain items
// or use shared ItemHandle for all chain items
if (item->getRefCount() > config_.thresholdForConvertingToIOBuf) {
auto sharedHdl = std::make_shared<Handle>(std::move(handle));
iobuf = folly::IOBuf{
folly::IOBuf::TAKE_OWNERSHIP, item,
// Since we'll be moving the IOBuf data pointer forward
// by dataOffset, we need to adjust the IOBuf length
// accordingly
dataOffset + item->getSize(),
[](void*, void* userData) {
auto* hdl = reinterpret_cast<std::shared_ptr<Handle>*>(userData);
delete hdl;
} /* freeFunc */,
new std::shared_ptr<Handle>{sharedHdl} /* userData for freeFunc */};
if (item->hasChainedItem()) {
converter = [sharedHdl](Item*, ChainedItem& chainedItem) {
const uint32_t chainedItemDataOffset = chainedItem.getOffsetForMemory();
return folly::IOBuf::takeOwnership(
&chainedItem,
// Since we'll be moving the IOBuf data pointer forward by
// dataOffset,
// we need to adjust the IOBuf length accordingly
chainedItemDataOffset + chainedItem.getSize(),
[](void*, void* userData) {
auto* hdl = reinterpret_cast<std::shared_ptr<Handle>*>(userData);
delete hdl;
} /* freeFunc */,
new std::shared_ptr<Handle>{sharedHdl} /* userData for freeFunc */);
};
}
} else {
// following IOBuf will take the item's ownership and trigger freeFunc to
// release the reference count.
handle.release();
iobuf = folly::IOBuf{folly::IOBuf::TAKE_OWNERSHIP, item,
// Since we'll be moving the IOBuf data pointer forward
// by dataOffset, we need to adjust the IOBuf length
// accordingly
dataOffset + item->getSize(),
[](void* buf, void* userData) {
Handle{reinterpret_cast<Item*>(buf),
*reinterpret_cast<decltype(this)>(userData)}
.reset();
} /* freeFunc */,
this /* userData for freeFunc */};
if (item->hasChainedItem()) {
converter = [this](Item* parentItem, ChainedItem& chainedItem) {
const uint32_t chainedItemDataOffset = chainedItem.getOffsetForMemory();
// Each IOBuf converted from a child item will hold one additional
// refcount on the parent item. This ensures that as long as the user
// holds any IOBuf pointing anywhere in the chain, the whole chain
// will not be evicted from cache.
//
// We can safely bump the refcount on the parent here only because
// we already have an item handle on the parent (which has just been
// moved into the IOBuf above). Normally, the only place we can
// bump an item handle safely is through the AccessContainer.
acquire(parentItem).release();
return folly::IOBuf::takeOwnership(
&chainedItem,
// Since we'll be moving the IOBuf data pointer forward by
// dataOffset,
// we need to adjust the IOBuf length accordingly
chainedItemDataOffset + chainedItem.getSize(),
[](void* buf, void* userData) {
auto* cache = reinterpret_cast<decltype(this)>(userData);
auto* child = reinterpret_cast<ChainedItem*>(buf);
auto* parent = &child->getParentItem(cache->compressor_);
Handle{parent, *cache}.reset();
} /* freeFunc */,
this /* userData for freeFunc */);
};
}
}
iobuf.trimStart(dataOffset);
iobuf.markExternallySharedOne();
if (item->hasChainedItem()) {
auto appendHelper = [&](ChainedItem& chainedItem) {
const uint32_t chainedItemDataOffset = chainedItem.getOffsetForMemory();
auto nextChain = converter(item, chainedItem);
nextChain->trimStart(chainedItemDataOffset);
nextChain->markExternallySharedOne();
// Append immediately after the parent, IOBuf will present the data
// in the original insertion order.
//
// i.e. 1. Allocate parent
// 2. add A, add B, add C
//
// In memory: parent -> C -> B -> A
// In IOBuf: parent -> A -> B -> C
iobuf.appendChain(std::move(nextChain));
};
forEachChainedItem(*item, std::move(appendHelper));
}
return iobuf;
}