bool Descriptor::PersistentMwCAS()

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