Memory management ================= Met.3D uses a lease-based memory management system to keep frequently used data in memory while evicting stale items when memory pressure rises. Overview -------- Data sources do not manage the lifetime of their produced items directly. Instead, every data item is handed to a memory manager via ``store()``, which immediately returns a *lease* to the data item, see ``MDataLease``. As long as at least one lease referencing an item is alive, the memory manager guarantees that the item will not be evicted. This means there is no window between item creation and first use during which the item could disappear. When all leases for an item are destroyed, the item becomes eligible for eviction according to the policy implemented by the concrete memory manager. The Memory Manager ------------------ ``MAbstractMemoryManager`` is the abstract base class for all memory managers. It defines the two primary operations: - ``store(key, item)``: Hands a freshly produced data item to the memory manager and returns a ``MDataLease`` that pins the item immediately. The caller owns the returned lease; the item is guaranteed alive for its lifetime. - ``acquire(key)``: Looks up an already-cached item by its key and returns a new lease pinning it. Returns an invalid lease if the key is not found. The base class also exposes ``isCached(key)`` to check for existence without acquiring a pin. Note that a race condition might occur if you call ``isCached(key)`` before ``acquire(key)``, as the item could be evicted between the two calls. Call ``acquire(key)`` directly and check whether the returned lease ``isValid()``. Concrete subclasses implement the eviction policy by overriding the ``pin()``, ``unpin()``, and ``evictToMakeSpace()`` methods. The ``MLRUMemoryManager`` ------------------------- ``MLRUMemoryManager`` is the standard implementation, applying a Least Recently Used eviction policy. It maintains two data structures: - A ``QMap`` that stores all items, both pinned and evictable. Each ``CacheEntry`` holds the ``MMemoryManagerItem`` control block, the last-access timestamp, and an iterator into the LRU list below. - A ``std::list`` (``evictableKeys``) that orders evictable items from most recently used (front) to least recently used (back). The iterator stored in each ``CacheEntry`` allows O(1) removal from any position in this list. Pinned items use ``evictableKeys.end()`` as a sentinel to indicate they are not in the eviction list. The memory limit is set at construction time. Attempting to store an item that exceeds the total capacity when no further items can be evicted (all have active leases) throws ``MMemoryError``. ``MDataLease`` and ``MDataLeaseBase`` ------------------------------------- ``MDataLeaseBase`` is an RAII handle representing a pin on a cached item. Constructing or copying a lease increments the item's pin count; destroying it decrements the count. When the count reaches zero, the item becomes evictable. The templated subclass ``MDataLease`` adds typed access via ``operator->``, providing access to the underlying data item. The parameter ``T`` must be a ``MAbstractDataItem``. The typeless base lease provides type elision. Typically, you will get the data lease from the data source producing the data item, using ``dataSource->getData(key)``. This will already return a typed lease. If you need to obtain a typed lease from an untyped ``MDataLeaseBase``, use ``dynamic_lease_cast``, similar to ``std::dynamic_pointer_cast``: .. code-block:: cpp :linenos: :emphasize-lines: 6, 8 MDataLeaseBase base = memoryManager->acquire(key); MDataLease lease = dynamic_lease_cast(base); if (lease) { /* safe to use lease->... */ } ``MMemoryManagerItem`` ---------------------- ``MMemoryManagerItem`` is the internal control block that the memory manager creates for each stored item. It holds the raw pointer to the data item, the current pin count (lease count), and the key. The control block is reference-counted via ``shared_ptr`` and outlives the data item itself: this is required because an item may be force-deleted when its producing data source is destroyed. After such a force deletion the lease remains safe to access, but ``isValid()`` returns ``false``, allowing callers to detect the situation without a dangling pointer. .. uml:: :caption: Sequence diagram depicting exemplary lifetime of a memory managed data item. :align: center participant A as "Actor" participant DS as "DataSource" participant MM as "MemoryManager" DS->>DS: create data item DS->>MM: store(key, item) MM-->>DS: DataLease [refcount=1] Note over MM: Item pinned while any lease alive A->>MM: acquire(key) MM-->>A: DataLease [refcount=2] DS->>MM: ~DataLease() [refcount=1] A->>MM: ~DataLease() [refcount=0] Note over MM: Item now evictable MM->>MM: remove item\non memory pressure