[ 
https://issues.apache.org/jira/browse/GROOVY-12307?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King resolved GROOVY-12307.
--------------------------------
    Fix Version/s: 6.0.0-beta-4
       Resolution: Fixed

> Indy: MetaClass change during call-site linking can be lost
> -----------------------------------------------------------
>
>                 Key: GROOVY-12307
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12307
>             Project: Groovy
>          Issue Type: Bug
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>             Fix For: 6.0.0-beta-4
>
>
> The GROOVY-12281 stress test intermittently fails with a per-thread 
> monotonicity violation:
> {noformat}
> ClassInfoSoftModeStressTest > 
> softModeSurvivesRealGcClearingUnderConcurrency() FAILED
>     stress probe failed: dispatches=3721845 generations=114 
> javaInfosCollected=33/36
>     ERROR: mutated-1: IllegalStateException: mutated-1 observed gen 13 after 
> already seeing 14
> {noformat}
> i.e. a dispatcher thread observed an older ExpandoMetaClass generation 
> *after* already observing a newer one. The stress environment (soft 
> ClassValue mode plus GC churn) only raises the relink frequency; the 
> underlying race is independent of soft mode.
> h3. Root cause
> Indy call-site linking reads MOP state and installs its guard in the wrong 
> order:
> # the fallback resolves the MetaClass and selects the target method;
> # only afterwards ({{Selector.setGuards}} -> 
> {{IndyInterface.applyMopSwitchPoints}}) does it fetch the *current* 
> class-domain SwitchPoint to guard that target.
> A MetaClass change landing between (1) and (2) retires the old SwitchPoint 
> and rotates in a fresh one — which is exactly the token the racing relink 
> then fetches. The stale selection is published under a live guard, clobbering 
> any newer target already installed at the shared call site. The site serves 
> the pre-change selection until an unrelated future invalidation; if the race 
> lands on the last change, the staleness is permanent (a MetaClass mutation is 
> silently lost).
> The same time-of-check/time-of-use hole exists in the cold reflective tier 
> ({{ColdReflectiveMethodHandleWrapper.tryBuild}} captures {{classValidity}} 
> after selection) and in the GEP-15 compound-assign resolver 
> ({{IndyCompoundAssign.resolve}} fetches the SwitchPoint after 
> {{respondsTo}}/selection).
> h3. Fix
> Capture the class-domain SwitchPoint *before* selection first reads MOP 
> state, and install the guard with that captured token: a change racing the 
> selection then invalidates the token, so the guard routes to the fallback and 
> re-links instead of publishing the stale selection.
> Two complications:
> * *Born-dead tokens:* lazy MetaClass creation during resolution bumps the 
> class generation, invalidating a token captured before the first-ever read 
> (an always-fallback guard then recurses to StackOverflowError). The capture 
> therefore retries: capture token, resolve the MetaClass, re-capture if the 
> token died during resolution — the next pass finds the cached instance and 
> converges.
> * *Pathological re-creation:* some resolutions re-create the MetaClass on 
> every lookup (e.g. ClosureMetaClass under 
> {{ExpandoMetaClass.enableGlobally()}}, which {{isValidWeakMetaClass}} always 
> rejects), so no pass can end with a live token and an unbounded retry 
> livelocks. The retry is bounded (3 passes); on exhaustion the selection 
> degrades to the existing uncacheable path — no guards, fresh selection per 
> call — which is correct and cannot livelock or recurse.
> A deterministic regression test in {{IndyScopedSwitchPointTest}} encodes the 
> contract (a token captured before a mid-selection mutation must route to the 
> fallback); the stress test remains the probabilistic end-to-end check.
> The classic (non-indy) {{CallSiteArray}} path has not been audited for an 
> analogous window and may warrant a follow-up.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to