Class NDAnchoredAttribute
- Namespace
- NumSharp
- Assembly
- NumSharp.dll
Marks a method that opens NO NDScope as an operand anchor: at build time the NumSharp IL
weaver roots each of its parameters that can reach an NDArray (and this, for an instance
method of a class) from the method's entry to every return, so an operand the caller passed as a temporary
cannot be collected while a kernel inside the call still reads its memory. Use it on an unscoped entry point
that hands an operand's raw address to a kernel; a NDScopedAttribute method already gets the
same rooting and must not carry this attribute too.
[AttributeUsage(AttributeTargets.Constructor|AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class NDAnchoredAttribute : Attribute
- Inheritance
-
NDAnchoredAttribute
- Inherited Members
Remarks
The hazard it closes ("lost GC anchors"). In optimized code the JIT reports a parameter to the GC
only up to its last use. A method that reads a.Address (or Storage.Address) and then runs a
kernel has no later use of a, so a caller that passes a temporary (np.copyto(t, np.arange(n)))
leaves the operand reachable from nothing during the kernel: a collection finalizes it, its ARC reference is
abandoned, and the buffer is pooled or freed under the kernel (wrong values, spurious errors, access
violations). The lifetime oracle (test/NumSharp.Tests.Oracle/Fuzz/Lifetime) reproduces the class.
What the weaver injects. At entry, each rooted parameter is copied into a shadow local (a reference
parameter as is; a struct that carries an NDArray, such as a tuple of arrays, boxed once); before every
ret, each shadow is passed to Anchor(object), a no-code intrinsic that extends its
lifetime to that point. Nothing is opened, tracked, disposed or allocated (except the box of an NDArray-carrying
struct), so the attribute fits hot entry points where a scope would cost too much. A reassigned parameter keeps
its ORIGINAL value rooted: the shadow is taken before the body runs.
What it does NOT root. Temporaries the method itself builds are not tracked (no scope is open):
a method that converts an operand (x = x.astype(...)) and then reads the converted array's address
after its last use needs NDScopedAttribute, whose scope tracks and roots everything constructed
during the call. Parameters that cannot reach an NDArray (numbers, enums, strings, Types,
spans and other by-ref-like structs) and ref/out/in parameters are left alone.
Rejected shapes (build error NDW019): a method without a body, an async method or an
iterator (its parameters are already hoisted into the state machine, which the caller holds), a body with a
tail call, a method that also carries NDScopedAttribute/NDScopedAsyncAttribute,
and a method with nothing to root.
Not inherited. The attribute applies to the method that declares it; mark each override that
needs rooting. Without the NumSharp.Build weaver the attribute is inert metadata (the analyzer warns
with NDW013), and a body that already calls Anchor(object) is left untouched (idempotence).