struct Cursor {
explicit Cursor(uint64_t initialTicket) noexcept : ticket(initialTicket) {}
- void moveForward(uint64_t steps = 1) noexcept {
+ /// Returns true if this cursor now points to a different
+ /// write, false otherwise.
+ bool moveForward(uint64_t steps = 1) noexcept {
+ uint64_t prevTicket = ticket;
ticket += steps;
+ return prevTicket != ticket;
}
- void moveBackward(uint64_t steps = 1) noexcept {
+ /// Returns true if this cursor now points to a previous
+ /// write, false otherwise.
+ bool moveBackward(uint64_t steps = 1) noexcept {
+ uint64_t prevTicket = ticket;
if (steps > ticket) {
ticket = 0;
} else {
ticket -= steps;
}
+ return prevTicket != ticket;
}
protected: // for test visibility reasons
// expose the cursor raw value via a wrapper type
template<typename T, template<typename> class Atom>
-uint64_t value(const typename LockFreeRingBuffer<T, Atom>::Cursor&& rbcursor) {
+uint64_t value(const typename LockFreeRingBuffer<T, Atom>::Cursor& rbcursor) {
typedef typename LockFreeRingBuffer<T,Atom>::Cursor RBCursor;
- RBCursor cursor = std::move(rbcursor);
-
struct ExposedCursor : RBCursor {
ExposedCursor(const RBCursor& cursor): RBCursor(cursor) {}
uint64_t value(){
return this->ticket;
}
};
- return ExposedCursor(cursor).value();
+ return ExposedCursor(rbcursor).value();
}
template<template<typename> class Atom>
EXPECT_EQ(3, cursorValue(rb.writeAndGetCursor(val)));
}
+TEST(LockFreeRingBuffer, moveBackwardsCanFail) {
+ const int capacity = 3;
+ LockFreeRingBuffer<int> rb(capacity);
+
+ // Workaround for template deduction failure
+ auto (&cursorValue)(value<int, std::atomic>);
+
+ int val = 0xfaceb00c;
+ rb.write(val);
+ rb.write(val);
+
+ auto cursor = rb.currentHead(); // points to 2
+ EXPECT_EQ(2, cursorValue(cursor));
+ EXPECT_TRUE(cursor.moveBackward());
+ EXPECT_TRUE(cursor.moveBackward()); // now at 0
+ EXPECT_FALSE(cursor.moveBackward()); // moving back does nothing
+}
+
} // namespace folly