/* * Copyright 2015-present Facebook, Inc. * * 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. */ #include #include #include #include #include namespace folly { TEST(LockFreeRingBuffer, writeReadSequentially) { const int capacity = 256; const int turns = 4; LockFreeRingBuffer rb(capacity); LockFreeRingBuffer::Cursor cur = rb.currentHead(); for (unsigned int turn = 0; turn < turns; turn++) { for (unsigned int write = 0; write < capacity; write++) { int val = turn * capacity + write; rb.write(val); } for (unsigned int write = 0; write < capacity; write++) { int dest = 0; ASSERT_TRUE(rb.tryRead(dest, cur)); ASSERT_EQ(turn * capacity + write, dest); cur.moveForward(); } } } TEST(LockFreeRingBuffer, writeReadSequentiallyBackward) { const int capacity = 256; const int turns = 4; LockFreeRingBuffer rb(capacity); for (unsigned int turn = 0; turn < turns; turn++) { for (unsigned int write = 0; write < capacity; write++) { int val = turn * capacity + write; rb.write(val); } LockFreeRingBuffer::Cursor cur = rb.currentHead(); cur.moveBackward(1); /// last write for (int write = capacity - 1; write >= 0; write--) { int foo = 0; ASSERT_TRUE(rb.tryRead(foo, cur)); ASSERT_EQ(turn * capacity + write, foo); cur.moveBackward(); } } } TEST(LockFreeRingBuffer, readsCanBlock) { // Start a reader thread, confirm that reading can block std::atomic readerHasRun(false); LockFreeRingBuffer rb(1); auto cursor = rb.currentHead(); cursor.moveForward(3); // wait for the 4th write const int sentinel = 0xfaceb00c; auto reader = std::thread([&]() { int val = 0; EXPECT_TRUE(rb.waitAndTryRead(val, cursor)); readerHasRun = true; EXPECT_EQ(sentinel, val); }); for (int i = 0; i < 4; i++) { EXPECT_FALSE(readerHasRun); int val = sentinel; rb.write(val); } reader.join(); EXPECT_TRUE(readerHasRun); } // expose the cursor raw value via a wrapper type template class Atom> uint64_t value(const typename LockFreeRingBuffer::Cursor& rbcursor) { typedef typename LockFreeRingBuffer::Cursor RBCursor; struct ExposedCursor : RBCursor { ExposedCursor(const RBCursor& cursor) : RBCursor(cursor) {} uint64_t value() { return this->ticket; } }; return ExposedCursor(rbcursor).value(); } template