mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-31 17:17:50 -05:00
Deleting PDM objects on a worker thread is not safe. PdmObjectHandle::prepareForDelete() mutates state owned by other objects, it nulls the guarded pointers held by other objects and clears m_pointersReferencingMe, an unsynchronised std::set that every PdmPointer construction and destruction touches. Destroying an object off the main thread therefore races with the main thread on the shared object graph. See issue 14491. Profiling a summary ensemble teardown shows the mechanism does not pay for itself. Releasing 397 Drogon realizations takes 0.24 s sequentially and 0.24 s in parallel, and for a heavy case the parallel release is slower than the sequential one, because free() is serialised inside the allocator. The PDM bookkeeping itself is 0.4 to 2.8 percent of the teardown. Remove the class and deleteChildrenAsync(), and delete synchronously instead. The call sites that used clearWithoutDelete() and a manual delete loop to work around the race can now call deleteChildren() directly. Add caf::PdmObjectHandleTools::deleteObjects() for the case where the objects are no longer owned by a child array field. The observer disconnection from issue 12262 does not depend on clearWithoutDelete(). ~Signal() unregisters itself from every observer, so a deleted child detaches itself. That fix addressed the async race, where ~Signal() mutated the observer list from a worker thread. The unit test is updated to assert the observed signal count directly instead of relying on a crash.
116 lines
4.0 KiB
C++
116 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include "cafPdmPtrArrayFieldHandle.h"
|
|
|
|
#include "cafAssert.h"
|
|
#include "cafPdmFieldHandle.h"
|
|
#include "cafPdmPointer.h"
|
|
|
|
#include <memory>
|
|
|
|
namespace caf
|
|
{
|
|
template <typename T>
|
|
class PdmFieldXmlCap;
|
|
|
|
//==================================================================================================
|
|
///
|
|
///
|
|
///
|
|
//==================================================================================================
|
|
class PdmChildArrayFieldHandle : public PdmPtrArrayFieldHandle
|
|
{
|
|
public:
|
|
PdmChildArrayFieldHandle() {}
|
|
~PdmChildArrayFieldHandle() override {}
|
|
|
|
virtual void deleteChildren() = 0;
|
|
|
|
bool hasSameFieldCountForAllObjects();
|
|
};
|
|
|
|
//==================================================================================================
|
|
/// PdmFieldClass to handle a collection of PdmObject derived pointers
|
|
/// The reasons for this class is to add itself as parentField into the objects being pointed to.
|
|
/// The interface is made similar to std::vector<>, and the complexity of the methods is similar too.
|
|
//==================================================================================================
|
|
|
|
template <typename DataType>
|
|
class PdmChildArrayField : public PdmFieldHandle
|
|
{
|
|
public:
|
|
PdmChildArrayField()
|
|
{
|
|
bool doNotUsePdmPointersFieldForAnythingButPointersToPdmObject = false;
|
|
CAF_ASSERT( doNotUsePdmPointersFieldForAnythingButPointersToPdmObject );
|
|
}
|
|
};
|
|
|
|
template <typename DataType>
|
|
class PdmChildArrayField<DataType*> : public PdmChildArrayFieldHandle
|
|
{
|
|
typedef DataType* DataTypePtr;
|
|
typedef std::unique_ptr<DataType> DataTypeUniquePtr;
|
|
|
|
public:
|
|
PdmChildArrayField() {}
|
|
~PdmChildArrayField() override;
|
|
|
|
PdmChildArrayField& operator()() { return *this; }
|
|
const PdmChildArrayField& operator()() const { return *this; }
|
|
|
|
// Reimplementation of PdmPointersFieldHandle methods
|
|
|
|
size_t size() const override { return m_pointers.size(); }
|
|
bool empty() const override { return m_pointers.empty(); }
|
|
void clearWithoutDelete() override;
|
|
void deleteChildren() override;
|
|
void insertAt( int indexAfter, PdmObjectHandle* obj ) override;
|
|
void insertAt( int indexAfter, std::unique_ptr<PdmObjectHandle> obj );
|
|
PdmObjectHandle* at( size_t index ) override;
|
|
void setValue( const std::vector<DataType*>& objects );
|
|
size_t indexOf( const PdmObjectHandle* obj ) const override;
|
|
|
|
// std::vector-like access
|
|
|
|
DataType* operator[]( size_t index ) const;
|
|
|
|
void push_back( DataTypePtr pointer );
|
|
void push_back( DataTypeUniquePtr pointer );
|
|
void set( size_t index, DataTypePtr pointer );
|
|
void set( size_t index, DataTypeUniquePtr pointer );
|
|
void insert( size_t indexAfter, DataTypePtr pointer );
|
|
void insert( size_t indexAfter, DataTypeUniquePtr pointer );
|
|
void insert( size_t indexAfter, const std::vector<PdmPointer<DataType>>& objects );
|
|
size_t count( const DataType* pointer ) const;
|
|
|
|
void erase( size_t index ) override;
|
|
|
|
typename std::vector<PdmPointer<DataType>>::iterator begin() { return m_pointers.begin(); };
|
|
typename std::vector<PdmPointer<DataType>>::iterator end() { return m_pointers.end(); };
|
|
|
|
typename std::vector<PdmPointer<DataType>>::const_iterator begin() const { return m_pointers.begin(); };
|
|
typename std::vector<PdmPointer<DataType>>::const_iterator end() const { return m_pointers.end(); };
|
|
|
|
// Child objects
|
|
std::vector<DataType*> childrenByType() const;
|
|
|
|
[[nodiscard]] std::vector<PdmObjectHandle*> children() const override;
|
|
void removeChild( PdmObjectHandle* object ) override;
|
|
|
|
private: // To be disabled
|
|
PDM_DISABLE_COPY_AND_ASSIGN( PdmChildArrayField );
|
|
|
|
private:
|
|
void removeThisAsParentField();
|
|
void addThisAsParentField();
|
|
|
|
private:
|
|
friend class PdmFieldXmlCap<PdmChildArrayField<DataType*>>;
|
|
std::vector<PdmPointer<DataType>> m_pointers;
|
|
};
|
|
|
|
} // End of namespace caf
|
|
|
|
#include "cafPdmChildArrayField.inl"
|