Fix potential deadlock in distributed contour tree

The `HierarchicalAugmenter` used in the distributed contour tree filter
attempts to save memory by releasing buffers used to send and receive
data after the DIY enqueues and dequeues are posted. This works as long
as the DIY serialization process copies the data in the arrays. However,
changes are coming where the data is sent directly from the buffers.
These changes cause a deadlock because the enqueue places a read lock
until the data is sent while the release tried to get a write lock.

The solution is simply to "forget" the array rather than explicitly
delete it. In this case, the array will automatically be deleted once
everyone is done with it.
This commit is contained in:
Kenneth Moreland 2023-03-23 07:07:44 -06:00
parent c9b4e9b7f2
commit 58fc4d8267
3 changed files with 11 additions and 25 deletions

@ -467,8 +467,12 @@ void HierarchicalAugmenter<FieldType>::RetrieveInAttachmentPoints()
template <typename FieldType>
void HierarchicalAugmenter<FieldType>::ReleaseSwapArrays()
{ // ReleaseSwapArrays()
this->OutData.ReleaseResources();
this->InData.ReleaseResources();
// Rather than explicitly delete the arrays, we are going to "forget" them and
// just release our reference count on them. If no one else is using them, the
// memory will actually be deleted. But if an array is being used, it will
// continue to be managed until it is not.
this->OutData = decltype(this->OutData){};
this->InData = decltype(this->InData){};
} // ReleaseSwapArrays()

@ -127,7 +127,10 @@ public:
blockData->HierarchicalAugmenter.PrepareOutAttachmentPoints(round);
// TODO/FIXME: Correct function? Correct round?
rp.enqueue(target, blockData->HierarchicalAugmenter.OutData);
// TODO/FIXME: Is it save to already release HierarchicalAugmenter.OutData (and InData) here. Don't we free the arrays before the other block had the chance to complete its rp.dequeue?
// Note: HierarchicalAugmenter.ReleaseSwapArrays() does not necessarily delete the
// arrays. Rather, it releases the reference to them. If, for example, the data
// for HierarchicalAugmenter.OutData is still in flight, the data will continue to
// exist until it is sent.
blockData->HierarchicalAugmenter.ReleaseSwapArrays();
}
}

@ -101,34 +101,13 @@ public:
}
/// Destructor
~HierarchicalAugmenterInOutData();
/// Clear all arrays
void ReleaseResources();
~HierarchicalAugmenterInOutData() = default;
/// Print contents fo this objects
std::string DebugPrint(std::string message, const char* fileName, long lineNum);
}; // class HierarchicalAugmenterInOutData
template <typename FieldType>
HierarchicalAugmenterInOutData<FieldType>::~HierarchicalAugmenterInOutData()
{
this->ReleaseResources();
}
// routine to release memory used for out arrays
template <typename FieldType>
void HierarchicalAugmenterInOutData<FieldType>::ReleaseResources()
{ // ReleaseResources()
this->GlobalRegularIds.ReleaseResources();
this->DataValues.ReleaseResources();
this->SupernodeIds.ReleaseResources();
this->Superparents.ReleaseResources();
this->SuperparentRounds.ReleaseResources();
this->WhichRounds.ReleaseResources();
} // ReleaseResources()
template <typename FieldType>
std::string HierarchicalAugmenterInOutData<FieldType>::DebugPrint(std::string message,
const char* fileName,