Class MemoryContextImpl

All Implemented Interfaces:
LifespanImpl.Addressed, Lifespan, MemoryContext

public class MemoryContextImpl extends LifespanImpl implements MemoryContext, LifespanImpl.Addressed
A lazily-created mirror of a PostgreSQL MemoryContext.

PostgreSQL is creating, resetting, and deleting memory contexts all the time, and most of them will never be visible in PL/Java; one of these objects only gets created when Java code specifically requests a reference to a particular context, generally to make it the Lifespan of some PL/Java object that should be invalidated when the context goes away.

Once an instance of this class has been instantiated and before it escapes to calling Java code, it must be registered for a reset/delete callback on the underlying PostgreSQL context so it can track its life cycle and invalidate, when the time comes, any objects it has been used as the "owner" of. (Instances that might be transiently created here, say in traversing the context tree, and won't escape, don't need the full registration treatment.) All creation, traversal, and mutation has to happen on the PG thread. Once published and while valid, an instance can be observed by other threads.

Events that can occur in the life of a memory context:

SetParent
It can be made a child of a context other than its original parent. (It can also be given the null parent, immediately before being deleted; this happens after invocation of the callback, though, so gives the callback routine no help in determining what is happening.)
Reset
It can have all of its descendant contexts deleted and its own allocations freed, but remain in existence itself.
ResetOnly
It can have its own allocations freed, with no effect on descendant contexts.
ResetChildren
All of its children can recursively get the ResetChildren treatment and in addition be ResetOnly themselves, but with no effect on this context itself.
Delete
All of its descendants, and last this context itself, go away.
DeleteChildren
All of its descendants go away, with no other effect on this context.

Complicating the lifecycle tracking, PostgreSQL will invoke exactly the same callback, with exactly the same parameter, whether the context in question is being deleted or reset. In the reset case, the context is still valid after the callback; in the delete case, it is not. The difference is not important for the objects "owned" by this context; they're to be invalidated in either case. But it leaves the callback with a puzzle to solve regarding what to do with this object itself.

A few related observations:

  • Within the callback itself, the context is still valid; its native struct may still be accessed safely, and its parent, child, and sibling links are sane.
  • If the firstchild link is non-null, this is definitely a reset and not a delete. In any delete case, all children will already be gone.
  • Conversely, though, absence of children does not prove this is deletion.
  • Hence, the callback will leave this mirror in either a definitely-valid or a maybe-deleted state.
  • In either state, its callback will have been deregistered. It must re-register the callback in the definitely-valid state. In the maybe-deleted state, it will receive no further callbacks, unless it can later be found revivifiable and the callback is re-registered.
  • Because the callback can only proceed when none of this ResourceOwner's owned objects are pinned, and they all will be invalidated and delinked from it, it will always be the owner of no objects when the callback completes. A possible approach then is to treat maybe-deleted as definitely-deleted always, invalidate and unpublish this object, and require a Java caller to obtain a new mirror of the same context if indeed it still exists and is wanted. Efforts to retain and possibly revivify the mirror could be viewed as optimizations. (They could have API consequences, though; without revivification, the object would have to be made invalid and throw an exception if used by Java code that had held on to a reference, even if only a reset was intended. Revivification could allow the retained reference to remain usable.)
  • Once the callback completes, the maybe-deleted state must be treated as completely forbidding any access to the mapped memory. If there is any information that could be useful in a later revivification decision, it must be collected by the callback and saved in the Java object state.
  • If the callback for a maybe-deleted mirror saves a reference to (a published Java mirror of) its parent at callback time and, at a later attempt to use the object, the parent is found to be valid and have this object as a child, revivification is supported.
  • That child-of-valid parent test can be applied recursively if the parent is also found to be maybe-deleted. But the test can spuriously fail if a (reset-but-still-valid) context was reparented after the callback saved its parent reference.
  • Obtaining the reference again from one of the PostgreSQL globals or from a valid PostgreSQL data structure clearly re-establishes that it is valid. (Whether it is "the same" context is more a philosophical point; whether reset or deleted, it was left with no allocations and no owned objects at that point, so questions of its "identity" may not be critical. Its name and ident may have changed. Its operations (the 'type' of context) may also have changed, but may be a lower-level detail than needs attention here.
  • Method Details

    • fromAddress

      public static MemoryContext fromAddress(long address)
    • getCurrentRaw

      public static long getCurrentRaw()
      Specialized method intended, so far, only for PgSavepoint's use.

      Only to be called on the PG thread.

    • setCurrentRaw

      public static void setCurrentRaw(long context)
      Even more specialized method intended, so far, only for PgSavepoint's use.

      Only to be called on the PG thread.

    • allocatingIn

      public static Checked.AutoCloseable<RuntimeException> allocatingIn(MemoryContext c)
      Change the current memory context to c, for use in a try-with-resources to restore the prior context on exit of the block.
    • address

      public long address()
      Specified by:
      address in interface LifespanImpl.Addressed
    • toString

      public String toString()
      Description copied from class: DualState
      Produce a string describing this state object in a way useful for debugging, with such information as the associated Lifespan and whether the state is fresh or stale.

      This method calls DualState.toString(Object) passing this. Subclasses are encouraged to override that method with versions that add subclass-specific details.

      Overrides:
      toString in class LifespanImpl
      Returns:
      Description of this state object.