This is an archive of the discontinued LLVM Phabricator instance.

[OpenMP][libomptarget] Do not rely on AsyncInfoWrapperTy's destructor to synchronize queue
ClosedPublic

Authored by kevinsala on Jan 29 2023, 3:02 PM.

Details

Summary

The AsyncInfoWrapperTy's mechanism does not work properly in some cases. I think it's due to copy elision. For instance, the behavior in (A) and (B) is quite different:

Case A:
=======
Error dataSubmit(void *TgtPtr, const void *HstPtr, int64_t Size, __tgt_async_info *AsyncInfo) {
  auto Err = Plugin::success();
  AsyncInfoWrapperTy AsyncInfoWrapper(Err, *this, AsyncInfo);
  Err = dataSubmitImpl(TgtPtr, HstPtr, Size, AsyncInfoWrapper);
  return Err;
}


Case B:
=======
Error dataSubmit(void *TgtPtr, const void *HstPtr, int64_t Size, __tgt_async_info *AsyncInfo) {
  // This check is added, and it seems it prevents copy elision to be applied.
  if (Size == 0)
    return Plugin::success();

  auto Err = Plugin::success();
  AsyncInfoWrapperTy AsyncInfoWrapper(Err, *this, AsyncInfo);
  Err = dataSubmitImpl(TgtPtr, HstPtr, Size, AsyncInfoWrapper);
  return Err;
}

In (A), there is no object Error constructed in dataSubmit; it uses an Error object at the caller function directly. Copy elision is applied. Everything works as expected. However, in (B), an object Error is constructed in dataSubmit and then moved to an Error object at the caller side before destroying local variables and returning. The order of actions when returning is the following:

  • Err is moved (using move constructor) to a new Error object in the caller function. Err is marked as checked.
  • AsyncInfoWrapper is destroyed. It may synchronize the queue/stream, and thus, re-set the Err object. However, Err was already moved, and nobody will check nor return it.
  • Destructor of Err fails because the error is not checked.

This patch makes the changes to stop relying on the AsyncInfoWrapperTy destructor to automatically synchronize the stream. Now, the error is provided directly by the wrapper (not a reference anymore) and there is a new function that will perform the synchronization if needed + returning the wrapper's error:

Error dataSubmit(void *TgtPtr, const void *HstPtr, int64_t Size, __tgt_async_info *AsyncInfo) {
  AsyncInfoWrapperTy AsyncInfoWrapper(*this, AsyncInfo);

  auto &Err = AsyncInfoWrapper.getError();
  Err = dataSubmitImpl(TgtPtr, HstPtr, Size, AsyncInfoWrapper);
  return AsyncInfoWrapper.finalize();
}

Diff Detail

Event Timeline

kevinsala created this revision.Jan 29 2023, 3:02 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 29 2023, 3:02 PM
kevinsala requested review of this revision.Jan 29 2023, 3:02 PM
jplehr added a subscriber: jplehr.Mar 15 2023, 4:53 AM

Looks reasonable and clean, @tianshilei1992 @jhuber6 ?

openmp/libomptarget/plugins-nextgen/common/PluginInterface/PluginInterface.cpp
281

Unrelated. If this is NFC, just commit this part w/o review.

tianshilei1992 accepted this revision.Mar 15 2023, 7:59 AM

Looks reasonable to me.

This revision is now accepted and ready to land.Mar 15 2023, 7:59 AM
kevinsala updated this revision to Diff 509034.Mar 28 2023, 8:41 AM

Removed unrelated change

ye-luo added a subscriber: ye-luo.Apr 5 2023, 7:37 AM
ye-luo added inline comments.
openmp/libomptarget/plugins-nextgen/common/PluginInterface/PluginInterface.cpp
936

I feel bad keeping Err as a member of AsyncInfoWrapper.

dataSubmitImpl sees AsyncInfoWrapper and return Err which actually operates on the member of AsyncInfoWrapper.

second issue if error already happened, finalize attempts a synchronization and overwrites the member Err which is bad for both attempting synchronization and overwriting it when there is already an error.

Can we remove the member Err of AsyncInfoWrapper completely and do
AsyncInfoWrapper.finalize(Err); // if Err is already bad, skip synchronization.
return Err;

ye-luo added inline comments.Apr 5 2023, 2:28 PM
openmp/libomptarget/plugins-nextgen/common/PluginInterface/PluginInterface.cpp
936

The above statement

second issue if error already happened, finalize attempts a synchronization and overwrites the member Err which is bad for both attempting synchronization and overwriting it when there is already an error.

It was not correct. The current code already checks Err and performs synchronization only when true.

kevinsala added inline comments.Apr 11 2023, 9:56 AM
openmp/libomptarget/plugins-nextgen/common/PluginInterface/PluginInterface.cpp
936

I just opened the patch D148027 to implement this change.