Back to Ponyc

Self-send object pinning spans codegen, the GC send/recv trace, and the sweep

.known-couplings/self-send-object-pinning.md

0.66.02.1 KB
Original Source

Self-send object pinning spans codegen, the GC send/recv trace, and the sweep

An actor that forwards a foreign object (owned by another actor) back to itself would otherwise have its per-actor GC sweep release that object to its owner every pass and re-borrow it on the next send, defeating weighted reference counting and flooding the owner with acquire messages. The fix pins such an object while it sits in the actor's own queue, via a protocol that crosses three areas and must stay in step: (1) gen_send_message (src/libponyc/codegen/gencall.c) passes the message's destination actor as the second argument of pony_gc_send; (2) pony_gc_send and pony_send_next (src/libponyrt/gc/trace.c) record it in pony_ctx_t.msg_target (pony_send_next does so after draining the previous message's trace stack, so a merged multi-destination round classifies each message under its own destination), and pony_send_done clears it; (3) in src/libponyrt/gc/gc.c, send_remote_object treats ctx->msg_target == ctx->current as a self-send and increments object_t.self_send_pins, recv_remote_object decrements it (before its dedup early-return, so it can never get stuck non-zero and leak), and move_unmarked_objects (src/libponyrt/gc/actormap.c) skips objects with self_send_pins > 0. A new message-send emission site that omits the destination, a pony_gc_send caller that passes the wrong actor, or a reordering of the target/trace/done sequence breaks self-send detection. The message-merge optimiser MergeMessageSend (genopt.cc) must preserve the destination operand when it rewrites a later message's pony_gc_send into pony_send_next (it does, via setCalledFunction) — that pass is currently dead (#5589), so this path is correct-by-construction but unexercised; verify it if the pass is revived. The reference-count accounting is unchanged, so a mistake here costs only acquire amortisation (or retains a wrongly-pinned object until the actor tears down), never incorrect collection. Run: make test-ci-core and, for the memory behaviour, the --pingers 1 self-forwarding case of the generative stress harness under test/rt-stress/generative.