BasicLinkedList::~BasicLinkedList() {
/* Delete stale items here, other items are deleted by the hash
table */
+ std::lock_guard<std::mutex> writeGuard(writeLock);
seqList.remove_and_dispose_if(
- [](const OrderedStoredValue& v) { return v.isStale(); },
+ [&writeGuard](const OrderedStoredValue& v) {
+ return v.isStale(writeGuard);
+ },
[this](OrderedStoredValue* v) {
this->st.currentSize.fetch_sub(v->metaDataSize());
delete v;
staleMetaDataSize.fetch_add(v->metaDataSize());
st.currentSize.fetch_add(v->metaDataSize());
- /* Safer to serialize with the deletion of stale values */
- std::lock_guard<std::mutex> lckGd(writeLock);
-
++numStaleItems;
- v->toOrderedStoredValue()->markStale();
+ {
+ std::lock_guard<std::mutex> writeGuard(writeLock);
+ v->toOrderedStoredValue()->markStale(writeGuard);
+ }
}
uint64_t BasicLinkedList::getNumStaleItems() const {
exptime == other.exptime && flags == other.flags &&
_isDirty == other._isDirty && deleted == other.deleted &&
newCacheItem == other.newCacheItem &&
- isOrdered == other.isOrdered && stale == other.stale &&
- nru == other.nru && getKey() == other.getKey());
+ isOrdered == other.isOrdered && nru == other.nru &&
+ getKey() == other.getKey());
}
void StoredValue::deleteImpl(HashTable& ht) {
deleted(itm.isDeleted()),
newCacheItem(true),
isOrdered(isOrdered),
- stale(false),
nru(itm.getNRUValue()) {
// Placement-new the key which lives in memory directly after this
// object.
deleted(other.deleted),
newCacheItem(other.newCacheItem),
isOrdered(other.isOrdered),
- stale(other.stale),
nru(other.nru) {
// Placement-new the key which lives in memory directly after this
// object.
bool deleted : 1;
bool newCacheItem : 1;
const bool isOrdered : 1; //!< Is this an instance of OrderedStoredValue?
- bool stale : 1; //!< indicates if a newer instance of the item is added
uint8_t nru : 2; //!< True if referenced since last sweep
static void increaseMetaDataSize(HashTable &ht, EPStats &st, size_t by);
class OrderedStoredValue : public StoredValue {
public:
// Intrusive linked-list for sequence number ordering.
+ // Guarded by the SequenceList's writeLock.
boost::intrusive::list_member_hook<> seqno_hook;
/**
* True if a newer version of the same key exists in the HashTable.
* Note: Only true for OrderedStoredValues which are no longer in the
* HashTable (and only live in SequenceList)
+ * @param writeGuard The locked SeqList writeLock which guards the stale
+ * param.
*/
- bool isStale() const {
+ bool isStale(std::lock_guard<std::mutex>& writeGuard) const {
return stale;
}
/**
* Marks that newer instance of this item is added in the HashTable
+ * @param writeLock The SeqList writeLock which guards the stale param.
*/
- void markStale() {
+ void markStale(std::lock_guard<std::mutex>& writeGuard) {
stale = true;
}
UniquePtr n,
EPStats& stats,
HashTable& ht)
- : StoredValue(itm, std::move(n), stats, ht, /*isOrdered*/ true) {
+ : StoredValue(itm, std::move(n), stats, ht, /*isOrdered*/ true),
+ stale(false) {
}
// Copy Constructor. Private, as needs to be carefully created via
UniquePtr n,
EPStats& stats,
HashTable& ht)
- : StoredValue(other, std::move(n), stats, ht) {
+ : StoredValue(other, std::move(n), stats, ht), stale(false) {
}
/* Do not allow assignment */
// Grant friendship to base class so it can perform flag dispatch to our
// overridden protected methods.
friend class StoredValue;
+
+ // indicates if a newer instance of the item is added. Guarded by the
+ // SequenceList's writeLock.
+ bool stale : 1;
};
SerialisedDocKey* StoredValue::key() {
return allSeqnos;
}
+ /// Expose the writeLock for testins.
+ std::mutex& getWriteLock() {
+ return writeLock;
+ }
+
/* Register fake read range for testing */
void registerFakeReadRange(seqno_t start, seqno_t end) {
std::lock_guard<SpinLock> lh(rangeLock);
basicLL->markItemStale(std::move(ownedSv));
/* Check if the StoredValue is marked stale */
- EXPECT_TRUE(nonOwnedSvPtr->isStale());
+ {
+ std::lock_guard<std::mutex> writeGuard(basicLL->getWriteLock());
+ EXPECT_TRUE(nonOwnedSvPtr->isStale(writeGuard));
+ }
/* Check if the stale count incremented to 1 */
EXPECT_EQ(1, basicLL->getNumStaleItems());
}
TEST(OrderedStoredValueTest, expectedSize) {
- EXPECT_EQ(72, sizeof(OrderedStoredValue))
+ // TODO-PERF: Ideally should be 72 - this is 63 bits larger than
+ // actually used due to moving {stale} from StoredValue's packed
+ // bitfields to OrderedStoredValue to prevent a race on a bitfield
+ // (TSan reports race between {stale} and {dirty} for example if they
+ // reside in the same byte).
+ // Better solution would be to pack this into the boost_list hook,
+ // or introduce per-OSV microlock to guard the whole object.
+ EXPECT_EQ(80, sizeof(OrderedStoredValue))
<< "Unexpected change in OrderedStoredValue fixed size";
auto item = make_item(0, makeStoredDocKey("k"), "v");
- EXPECT_EQ(75, OrderedStoredValue::getRequiredStorage(item))
+ EXPECT_EQ(83, OrderedStoredValue::getRequiredStorage(item))
<< "Unexpected change in OrderedStoredValue storage size for item: "
<< item;
}