in src/mwcas/mwcas.cc [627:756]
bool Descriptor::PersistentMwCAS(uint32_t calldepth) {
DCHECK(owner_partition_->garbage_list->GetEpoch()->IsProtected());
// Not visible to anyone else, persist before making the descriptor visible
if(calldepth == 0) {
RAW_CHECK(status_ == kStatusUndecided, "invalid status");
NVRAM::Flush(sizeof(Descriptor), this);
}
auto status = status_;
if(status & kStatusDirtyFlag) {
PersistStatus();
CompareExchange32(&status_, status & ~kStatusDirtyFlag, status);
status &= ~kStatusDirtyFlag;
}
if(status != kStatusUndecided) {
if(calldepth > 0) {
// Operation has already concluded, return.
MwCASMetrics::AddBailedHelp();
return status == kStatusSucceeded;
} else {
return Cleanup();
}
}
uint64_t descptr = SetFlags(this, kMwCASFlag | kDirtyFlag);
uint32_t my_status = kStatusSucceeded;
#ifdef RTM
// Go to phase 2 directly if helping along.
if(calldepth > 0 && !RTMInstallDescriptors(kDirtyFlag)) {
my_status = kStatusFailed;
}
#else
std::sort(words_, words_ + count_, [this](WordDescriptor &a, WordDescriptor &b)->bool{
return a.address_ < b.address_;
});
for(uint32_t i = 0; i < count_ && my_status == kStatusSucceeded; ++i) {
WordDescriptor* wd = &words_[i];
if((uint64_t)wd->address_ == Descriptor::kAllocNullAddress){
continue;
}
retry_entry:
auto rval = CondCAS(i, kDirtyFlag);
// Ok if a) we succeeded to swap in a pointer to this descriptor or b) some
// other thread has already done so. Need to persist all fields (which point
// to descriptors) before switching to final status, so that recovery will
// know reliably whether to roll forward or back for this descriptor.
if(rval == wd->old_value_ || CleanPtr(rval) == (uint64_t)this) {
continue;
}
if (rval & kDirtyFlag){
goto retry_entry;
}
// Do we need to help another MWCAS operation?
if(IsMwCASDescriptorPtr(rval)) {
if(rval & kDirtyFlag) {
wd->PersistAddress();
CompareExchange64(wd->address_, rval & ~kDirtyFlag, rval);
}
// Clashed with another MWCAS; help complete the other MWCAS if it is
// still in flight.
Descriptor* otherMWCAS = (Descriptor*)CleanPtr(rval);
otherMWCAS->PersistentMwCAS(calldepth + 1);
MwCASMetrics::AddHelpAttempt();
goto retry_entry;
} else {
// rval must be another value, we failed
my_status = kStatusFailed;
}
}
#endif
// Persist all target fields if we successfully installed mwcas descriptor on
// all fields.
if(my_status == kStatusSucceeded) {
for (uint32_t i = 0; i < count_; ++i) {
WordDescriptor* wd = &words_[i];
if((uint64_t)wd->address_ == Descriptor::kAllocNullAddress){
continue;
}
uint64_t val = *wd->address_;
if(val == descptr) {
wd->PersistAddress();
CompareExchange64(wd->address_, descptr & ~kDirtyFlag, descptr);
}
}
}
// Switch to the final state, the MwCAS concludes after this point
CompareExchange32(&status_, my_status | kStatusDirtyFlag, kStatusUndecided);
// Now the MwCAS is concluded - status is either succeeded or failed, and
// no observers will try to help finish it, so do a blind flush and reset
// the dirty bit.
RAW_CHECK((status_ & ~kStatusDirtyFlag) != kStatusUndecided, "invalid status");
PersistStatus();
status_ &= ~kStatusDirtyFlag;
// No need to flush again, recovery does not care about the dirty bit
bool succeeded = (status_ == kStatusSucceeded);
for(uint32_t i = 0; i < count_; i++) {
WordDescriptor* wd = &words_[i];
if((uint64_t)wd->address_ == Descriptor::kAllocNullAddress){
continue;
}
uint64_t val = succeeded ? wd->new_value_ : wd->old_value_;
val |= kDirtyFlag;
uint64_t clean_descptr = descptr & ~kDirtyFlag;
if(clean_descptr == CompareExchange64(wd->address_, val, descptr)) {
// Retry if someone else already cleared the dirty bit
CompareExchange64(wd->address_, val, clean_descptr);
}
wd->PersistAddress();
RAW_CHECK(val & kDirtyFlag, "invalid final value");
CompareExchange64(wd->address_, val & ~kDirtyFlag, val);
}
if(calldepth == 0) {
return Cleanup();
} else {
return succeeded;
}
}