Skip to content

Commit

Permalink
Document new internal function
Browse files Browse the repository at this point in the history
  • Loading branch information
franzpoeschel committed Jul 17, 2024
1 parent c72bcf8 commit 1a18e76
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 2 deletions.
12 changes: 12 additions & 0 deletions include/openPMD/backend/Attributable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ namespace internal

class RecordComponentData;

/*
* Internal function to turn a handle into an owning handle that will keep
* not only itself, but the entire Series alive. Works by hiding a copy of
* the Series into the destructor lambda of the internal shared pointer. The
* returned handle is entirely safe to use in just the same ways as a normal
* handle, just the surrounding Series needs not be kept alive any more
* since it is stored within the handle. By storing the Series in the
* handle, not in the actual data, reference cycles are avoided.
*
* Instantiations for T exist for types RecordComponent,
* MeshRecordComponent, Mesh, Record, ParticleSpecies, Iteration.
*/
template <typename T>
T &makeOwning(T &self, Series);
} // namespace internal
Expand Down
31 changes: 29 additions & 2 deletions src/backend/Attributable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,39 @@ namespace internal
template <typename T>
T &makeOwning(T &self, Series s)
{
/*
* `self` is a handle object such as RecordComponent or Mesh (see
* instantiations below).
* These objects don't normally keep alive the Series, i.e. as soon as
* the Series is destroyed, the handle becomes invalid.
* This function modifies the handle such that it actually keeps the
* Series alive and behaves otherwise identically.
* First, get the internal shared pointer of the handle.
*/
std::shared_ptr<typename T::Data_t> data_ptr = self.T::getShared();
auto raw_ptr = data_ptr.get();
/*
* Now, create a new shared pointer pointing to the same address as the
* actual pointer and replace the old internal shared pointer by the new
* one.
*/
self.setData(std::shared_ptr<typename T::Data_t>{
raw_ptr,
[s_lambda = std::move(s), data_ptr_lambda = std::move(data_ptr)](
auto const *) { /* no-op */ }});
/*
* Here comes the main trick.
* The new shared pointer stores (and thus keeps alive) two items
* via lambda capture in its destructor:
* 1. The old shared pointer.
* 2. The Series.
* It's important to notice that these two items are only stored
* within the newly created handle, and not internally within the
* actual openPMD object model. This means that no reference cycles
* can occur.
*/
[s_lambda = std::move(s),
data_ptr_lambda = std::move(data_ptr)](auto const *) {
/* no-op, the lambda captures simply go out of scope */
}});
return self;
}

Expand Down

0 comments on commit 1a18e76

Please sign in to comment.