bool Descriptor::PersistentMwCASWithFailure()

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;
}