Ephemeral persistent objects for selecting pieces of components

Purpose

At least a couple use cases we have encountered (selecting cells of a mesh for grow or split operations, selecting placements of a model instance for editing) need to create new persistent objects (mainly components) inside a resource, but want those objects to be removed once users no longer need them.

The problem is that removing the object must be done with an operation so that the Qt GUI layer can be signaled before the object or its shared pointer become invalid.

Design Alternative A

Use shared-pointer constructors that take a deleter so that an operation can be invoked by the deleter.

A prototype is implemented in SMTK MR 1670, but updating this to work with the new selection framework in SMTK MR 1704 has lead to some issues with resource locking.

Design Alternative B

Create a garbage-collector class that owns ephemeral objects and removes them when they are abandoned. The garbage collector might be created on demand by an operation or resource manager and look like this:

class SMTKCORE_EXPORT GarbageCollector : smtkEnableSharedPtr(GarbageCollector)
{
public:
  smtkTypeMacroBase(smtk::resource::GarbageCollector);
  virtual ~GarbageCollector();

  /// Queue an operation to clean up an ephemeral resource.
  bool add(const smtk::operation::OperationPtr& deleter);

protected:
  GarbageCollector();

  static void collectGarbage();

  /// The set of operations used to collect garbage
  /// (and whose associations indicate which objects
  /// are garbage).
  std::set<smtk::operation::OperationPtr> m_garbage;
};

This class would queue an operation (which you provide to add()) to delete the ephemeral objects associated with the operation when their shared-pointer use-count drops to 1 (indicating they are owned solely by the resource managing them). The use-counts would be inspected each time a registered operation manager invokes the collectGarbage() method via an observer – iterating over its set of delete-operations’ associations and queuing the first delete-operation that matches the criteria.

Note that the queued deletion operations must

  • accept associations,
  • prevent their associations from holding references (i.e., have "HoldReferences"="false" on the associations so associated objects are held weakly), and
  • require at least 1 associated persistent object.

Note that an operation, A, passed to the garbage collector is not guaranteed to run; some other operation, B, may delete the associated objects first. In that case, when B completes successfully, the garbage collector will run, see that A has no valid associated objects (since they are weak references to the deleted object), and remove operation A from its list of collectors. It is also possible A will never be invoked because the program exits while holding references.

Discussion

The difference between the designs above

  • Design A does not require iteration over deletion operations as other operations complete.
  • Design B invokes the deletion operations at a point when the resources still own a shared pointer to the ephemeral object, while in Design A all weak pointers to the ephemeral object will already be invalidated.