7 Make ownership and lifecycles explicit

Ownership becomes interesting when something disappears. A caller disconnects, an agent is cancelled, a process restarts, or a request times out. The system still has resources, effects, and obligations. Who owns them now?

Three meanings of ownership often become entangled. Object ownership concerns who keeps a value alive. Operational responsibility concerns who must finish or clean up work. Authority concerns who may decide that an action should happen. A reference-counted pointer can solve the first while leaving the other two unresolved.

In the resource example, a consumer may request acquisition without owning the shared transfer. A service may own the transfer without being authorized to select a substitute resource. A background effect may retain a workspace after the requesting future has been dropped. The design needs to express these differences.

Follow the effect rather than the requester

A concrete Pumas change makes the issue visible. In the inspected draft acquisition work, a blocking effect could continue after its asynchronous waiter and callback disappeared. The registered effect did not itself retain the workspace reservation. The repair captured the workspace inside that effect, so the activity still able to touch the resource retained its claim until it returned.

The associated regression uses an actual exclusive lock, holds the blocking work, cancels the waiter, and attempts a competing acquisition of the lock. The competing handle must fail while the effect remains active and succeed after it finishes. Source and test assertions were inspected, and local results were recorded by the project. This is bounded evidence for the ownership rule, not comprehensive production or cross-platform qualification. Repair; regression at the inspected head.

The general question is simple: which activity can still produce a side effect? That activity needs the relevant resource claim, even if the person who originally asked for it has stopped waiting.

Describe transitions with obligations

A list of states is not enough. Each transition should explain its precondition, effect, authority, and outstanding obligation.

For example, moving a candidate from checking to ready may require all readiness checks to pass, publication to occur, and the consumer reference to resolve. Moving from transferring to cancel requested may only record intent to stop. The latter state must not imply that the worker has stopped touching its workspace.

A useful transition record could say:

  • Before: candidate exists, no ready reference has been published
  • Decision owner: acquisition service under the caller's admitted request
  • Required evidence: specified checks passed for this candidate identity
  • Effect: publish the ready reference
  • After: consumers may use the candidate; cleanup responsibility for superseded storage is separately defined

The cleanup clause prevents a common omission. Successful publication may leave work behind. If the caller's response ends the only lifetime that retained cleanup responsibility, success becomes a resource leak.

Shared demand changes cancellation

Suppose two consumers need the same resource. Cancelling one consumer's request need not mean cancelling the shared work. The system may remove that consumer's demand while continuing for the other.

This is a different domain from one request owning one task. Reusing a cancellation token without examining its meaning can make the first departing consumer disrupt everyone else. Conversely, never cancelling shared work can waste resources after all demand disappears.

Model the demand separately when the distinction matters. Decide who counts active consumers, how stale demand is retired, and what happens when new demand arrives during shutdown. There may be races that require one serialized owner or a compare-and-update operation. The solution should follow the invariant, not a preference for a particular concurrency primitive.

Make uncertain outcomes expressible

A timeout can mean that the observer stopped waiting, not that the operation failed. If publication may already have happened, blindly retrying can duplicate effects. The next action should usually inspect authoritative state or use an operation whose retry semantics account for identity.

An explicit uncertain state can be useful, but it should have a resolution path. A system that records uncertainty forever without allowing reconciliation has described the problem without making it operable.

For your own project, choose one resource that outlives a request: a process, file lock, network operation, temporary directory, or agent task. Draw the lifetimes of the caller, effect, and cleanup owner. Terminate the caller in the model. If an obligation has no remaining owner, the design is incomplete. If several owners can independently release the same resource, inspect that boundary too.

Search the book

Type to find a passage.