fix(D-W6): three diagnostic env-flags + sharper Class::MOP drift diagnosis#607
Open
fix(D-W6): three diagnostic env-flags + sharper Class::MOP drift diagnosis#607
Conversation
Adds three off-by-default debug env-flags to instrument the cooperative refcount system: - PJ_DESTROY_TRACE=1 — log every DestroyDispatch.callDestroy entry with class, identity hash, refCount, destroyFired, and stack. - PJ_PENDING_TRACE=1 — at every MortalList.flush() start, log duplicate identities in `pending`. - PJ_WEAKCLEAR_TRACE=1 — log every WeakRefRegistry.clearWeakRefsTo call for blessed objects with caller stack. All zero-cost when disabled. Combined trace output on `use Class::MOP` (with the gate disabled to surface the bug) shows: - A Class::MOP::Class instance reaches refCount=0 inside a MortalList.flush() call from inside an anon CV at Class/MOP/Class.pm:260. - The first destroy (NOT a double-destroy — the second is a destroyFired cleanup pass) clears weak refs. - That weak-ref clear wipes the Class::MOP::Attribute's `associated_class` weakened ref, making it read as undef in `_remove_accessor`, which is the proximate cause of the "Can't call method get_method" failure. So the real bug is that some scope-exit is queueing a decrement on the metaclass that brings refCount legitimately to 0 — i.e. the cooperative refcount thinks `our %METAS` is NOT a strong holder. The next session should add a "log every refCount decrement of Class::MOP::Class" probe to find which scope's exit takes refCount from 1 to 0. The walker gate is restored (matches master) — these are pure diagnostic additions. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds three off-by-default debug env-flags to instrument the cooperative
refcount system. Combined trace output on
use Class::MOP(with thegate temporarily disabled to surface the bug) sharpens the D-W6
diagnosis significantly.
Diagnostic env-flags
PJ_DESTROY_TRACE=1DestroyDispatch.callDestroyentry: class, identity, refCount, destroyFired, stackPJ_PENDING_TRACE=1MortalList.flush()start, log duplicate identities inpendingPJ_WEAKCLEAR_TRACE=1WeakRefRegistry.clearWeakRefsTofor blessed objects with caller stackAll zero-cost when disabled. The walker gate is restored — these are pure
diagnostic additions, no production behaviour change.
What the traces revealed
Running
PJ_PENDING_TRACE=1 PJ_DESTROY_TRACE=1 PJ_WEAKCLEAR_TRACE=1 ./jperl -e 'use Class::MOP':A
Class::MOP::Classinstance reachesrefCount=0inside alegitimate
MortalList.flush()call from inside an anonymous CV atClass/MOP/Class.pm:260. The trace saysdestroyFired=false—this is the first destroy of the metaclass.
pendingcontains the same metaclass identity multiple times(counted via
[PENDING-DUP]), but each duplicate corresponds to areal strong reference (refCount=N matches count). So the drift is
NOT a duplicate-add bug.
The destroy clears
Class::MOP::Class's weak refs, which wipesClass::MOP::Attribute::associated_class— the proximate cause ofthe bootstrap failure.
What this rules in / out
destroy alone is sufficient to clear weak refs.
ModuleInitGuard.perspective. Some N pending decrements brought refCount from N to 0.
The decrements are real — the cooperative count thinks no one
strongly holds the metaclass.
So the real bug is upstream: someone is queueing a decrement on
the Class::MOP::Class metaclass that they shouldn't be, OR
our %METASis not registering as a strong holder.Concrete next leads (sharper than D-W6.4 round 1)
Documented in
dev/modules/moose_support.md:Audit
RuntimeHash.putand the package-global hash storepath.
Class::MOP::store_metaclass_by_namedoes$METAS{$pkg} = $metafrom inside a function — that path mayskip the refCount increment that
hash_slot.texercises fromthe caller scope.
Trace every
refCountdecrement ofClass::MOP::Classinstances with a stack trace. The decrement that takes
refCount 1 → 0 is the smoking gun.
Resolve
Class/MOP/Class.pm:260's closure to its Perlsource position to identify which scope-exit is leaking the
decrement.
Test plan
make(build + unit tests) green.masterbaseline behaviour unchanged (gate restored, noproduction paths altered).
Open D-W6 PR backlog (unchanged)
safety net.
identified.
hypotheses.
hypotheses.
Generated with Devin
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>