Commit fe381c8b authored by Tudor Bosman's avatar Tudor Bosman Committed by Praveen Kumar Ramakrishnan

IOBuf::reserve would return less tailroom than requested under certain circumstances

Test Plan: test added

Reviewed By: lxiong@fb.com

Subscribers: lxiong, net-systems@, folly-diffs@, yfeldblum, chalfant, pamelavagata, kma

FB internal diff: D2036967

Tasks: 6925950

Signature: t1:2036967:1430431606:3e115f7ed76b207572db26d352bebefe7a3d306d
parent e6029638
...@@ -698,7 +698,7 @@ void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) { ...@@ -698,7 +698,7 @@ void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) {
return; return;
} }
size_t newAllocatedCapacity = goodExtBufferSize(newCapacity); size_t newAllocatedCapacity = 0;
uint8_t* newBuffer = nullptr; uint8_t* newBuffer = nullptr;
uint64_t newHeadroom = 0; uint64_t newHeadroom = 0;
uint64_t oldHeadroom = headroom(); uint64_t oldHeadroom = headroom();
...@@ -708,8 +708,9 @@ void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) { ...@@ -708,8 +708,9 @@ void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) {
SharedInfo* info = sharedInfo(); SharedInfo* info = sharedInfo();
if (info && (info->freeFn == nullptr) && length_ != 0 && if (info && (info->freeFn == nullptr) && length_ != 0 &&
oldHeadroom >= minHeadroom) { oldHeadroom >= minHeadroom) {
if (usingJEMalloc()) {
size_t headSlack = oldHeadroom - minHeadroom; size_t headSlack = oldHeadroom - minHeadroom;
newAllocatedCapacity = goodExtBufferSize(newCapacity + headSlack);
if (usingJEMalloc()) {
// We assume that tailroom is more useful and more important than // We assume that tailroom is more useful and more important than
// headroom (not least because realloc / xallocx allow us to grow the // headroom (not least because realloc / xallocx allow us to grow the
// buffer at the tail, but not at the head) So, if we have more headroom // buffer at the tail, but not at the head) So, if we have more headroom
...@@ -743,6 +744,7 @@ void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) { ...@@ -743,6 +744,7 @@ void IOBuf::reserveSlow(uint64_t minHeadroom, uint64_t minTailroom) {
// None of the previous reallocation strategies worked (or we're using // None of the previous reallocation strategies worked (or we're using
// an internal buffer). malloc/copy/free. // an internal buffer). malloc/copy/free.
if (newBuffer == nullptr) { if (newBuffer == nullptr) {
newAllocatedCapacity = goodExtBufferSize(newCapacity);
void* p = malloc(newAllocatedCapacity); void* p = malloc(newAllocatedCapacity);
if (UNLIKELY(p == nullptr)) { if (UNLIKELY(p == nullptr)) {
throw std::bad_alloc(); throw std::bad_alloc();
......
...@@ -1081,6 +1081,33 @@ TEST(IOBuf, HashAndEqual) { ...@@ -1081,6 +1081,33 @@ TEST(IOBuf, HashAndEqual) {
EXPECT_EQ(hash(e), hash(f)); EXPECT_EQ(hash(e), hash(f));
} }
// reserveSlow() had a bug when reallocating the buffer in place. It would
// preserve old headroom if it's not too much (heuristically) but wouldn't
// adjust the requested amount of memory to account for that; the end result
// would be that reserve() would return with less tailroom than requested.
TEST(IOBuf, ReserveWithHeadroom) {
// This is assuming jemalloc, where we know that 4096 and 8192 bytes are
// valid (and consecutive) allocation sizes. We're hoping that our
// 4096-byte buffer can be expanded in place to 8192 (in practice, this
// usually happens).
const char data[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit";
constexpr size_t reservedSize = 24; // sizeof(SharedInfo)
// chosen carefully so that the buffer is exactly 4096 bytes
IOBuf buf(IOBuf::CREATE, 4096 - reservedSize);
buf.advance(10);
memcpy(buf.writableData(), data, sizeof(data));
buf.append(sizeof(data));
EXPECT_EQ(sizeof(data), buf.length());
// Grow the buffer (hopefully in place); this would incorrectly reserve
// the 10 bytes of headroom, giving us 10 bytes less than requested.
size_t tailroom = 8192 - reservedSize - sizeof(data);
buf.reserve(0, tailroom);
EXPECT_LE(tailroom, buf.tailroom());
EXPECT_EQ(sizeof(data), buf.length());
EXPECT_EQ(0, memcmp(data, buf.data(), sizeof(data)));
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, true); gflags::ParseCommandLineFlags(&argc, &argv, true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment