← board

GetInterface / Supports hand back a borrowed reference into a managed slot

The asymmetry

__pxxGetInterface(Instance, IID, Obj) writes through a raw Pointer:

outp := PPxxPtr_(Obj);
outp^ := Instance;

No _AddRef. The write is invisible to the managed-variable machinery, so:

So a successful Supports(o, IFoo, f) in a procedure body is one release against zero retains. With a TInterfacedObject whose lifetime is owned elsewhere this is invisible; with one whose only reference is f, it is an early free.

Why it has not bitten

Every use in this repo's tests and corpora reaches the object through some other owning reference, so the count never crosses zero at the wrong moment. The differential that found the GUID bug also could not make it crash — a contrived attempt produced a divergence in FPC's favour only because FPC's own Supports releases its temporary and destroys the object, which is the correct behaviour pxx does not reproduce.

Options

  1. AddRef in __pxxGetInterface on success, and release the old value first. Correct, and it makes the slot a real owning reference — but it needs the old value to be a real owning reference too, which it is not today for a slot a previous GetInterface filled. Sequencing matters.
  2. Lower Supports/GetInterface through the managed-assignment path instead of a raw store, so the ordinary retain/release rules apply and the helper stops touching the slot directly. Bigger, and the right shape.
  3. Leave it and document the borrow. Defensible only if the dialect states it, which would diverge from FPC.

Recommendation: option 2, with option 1 as the cheap interim if something starts crashing. Either way it wants a repro first — a TInterfacedObject whose sole reference comes from Supports and which is expected to survive to the end of the scope.