in src/mwcas/mwcas.cc [758:859]
bool Descriptor::PersistentMwCASWithFailure(uint32_t calldepth,
bool complete_descriptor_install) {
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) {
MwCASMetrics::AddBailedHelp();
return status == kStatusSucceeded;
} else {
return Cleanup();
}
}
uint64_t descptr = SetFlags(this, kMwCASFlag | kDirtyFlag);
uint32_t my_status = kStatusSucceeded;
#ifdef RTM
if(calldepth > 0 && !RTMInstallDescriptors(kDirtyFlag)) {
my_status = kStatusFailed;
}
#else
for(uint32_t i = 0; i < count_ && my_status == kStatusSucceeded; ++i) {
WordDescriptor* wd = &words_[i];
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 being worked on.
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];
uint64_t val = *wd->address_;
if(val == descptr) {
wd->PersistAddress();
CompareExchange64(wd->address_, descptr & ~kDirtyFlag, descptr);
}
}
}
// The compare exchange below will determine whether the mwcas will roll
// forward or back on recovery. If we are told to not complete descriptor
// install, exit the function before updating the operation status. Otherwise
// update the status but do not perform the final phase of the mwcas
// (installing final values in place of the descriptors and memory cleanup).
if (!complete_descriptor_install) {
return false;
}
// Switch to the final state, the MwCAS concludes after this point
CompareExchange32(&status_, my_status | kStatusDirtyFlag, kStatusUndecided);
RAW_CHECK((status_ & ~kStatusDirtyFlag) != kStatusUndecided, "invalid status");
PersistStatus();
status_ &= ~kStatusDirtyFlag;
// Always return false, since this function is used to test failure paths.
return false;
}