Summary
IO::Async 0.805 regressed from PASS to FAIL in CPAN random-tester run 20260826-133506-83222. The report is recorded as a 300-second timeout, but the suite had already completed several failing tests before the timeout. The failures are reproducible reference-count mismatches in both PerlOnJava backends.
There are two related compatibility problems:
B::SV::REFCNT reports an extra owner for otherwise singly-owned IO::Async::Loop objects (expected 1, found 2).
- The PerlOnJava patch for
t/05notifier-loop.t still expects a historical extra notifier owner (3), while current PerlOnJava reports 2, which now matches system Perl.
The overall 300-second cap is a secondary concern: IO::Async deliberately contains multi-second timer tests, and the archived run was still making progress. This is not evidence of a hang.
Distribution and configuration
- Distribution:
PEVANS/IO-Async-0.805.tar.gz
- Previous result: PASS on 2026-05-12 at PerlOnJava commit
b5c01ce1c
- Current result: REGRESS on 2026-08-26
- Distropref patches applied:
IO-Async/NoFork.patch
IO-Async/PerlOnJava.patch
IO-Async/SkipUnsupportedSocketTests.patch
- Distropref environment:
IO_ASYNC_NO_FORK=1
Configuration and build both succeed. The failures occur during Build test.
Focused results
System Perl
Using the same IO::Async 0.805 source and required test dependencies:
t/05notifier-loop.t: PASS, 35/35
t/11loop-poll-timer.t: PASS, 19/19
The final native-Perl expectation in t/05notifier-loop.t is 2 references.
PerlOnJava JVM backend
t/05notifier-loop.t:
not ok 35 - $loop has PerlOnJava notifier owner finally
# expected 3 references, found 2
t/11loop-poll-timer.t:
not ok 3 - $loop has refcount 1
# expected 1 references, found 2
not ok 5 - $loop has refcount 1 after watch_time
# expected 1 references, found 2
not ok 19 - $loop has refcount 1 finally
# expected 1 references, found 2
PerlOnJava interpreter backend
Both focused tests produce the same failures and counts as the JVM backend. This points to shared runtime/refcount behavior rather than backend-specific compilation.
Other failures visible before timeout
The archived CPAN run also reports expected 1, found 2 in both Poll and Select variants of:
- timer tests
- signal tests
- idle tests
- control tests
- metrics tests
The run reached tests after t/22timer-absolute.t before the tester's 300-second total-runtime limit fired. Therefore the timeout classification masks real completed failures.
Likely regression area
History and blame point to commit fe9d6bc5f (feat: complete Perl ithread lifecycle compatibility, 2026-08-15). That commit changed B::SV::REFCNT to call:
Internals::SvREFCNT($_[0]->{ref}, 1)
and added a special case in Internals.svRefcount() that increments the reported count when:
rc == 2
&& the B-specific flag is true
&& !ReachabilityWalker.hasLiveStrongScalarReferentOtherThan(base, arg)
The special case was introduced to keep a real aggregate owner visible in DBIx::Class/thread lifecycle paths. It appears to misclassify the temporary/private owner created by B::SV for these IO::Async loop objects, producing 2 where native Perl and IO::Async expect 1.
This regression point is inferred from the focused behavior and source history; it has not yet been confirmed by a commit-by-commit bisect.
The opposite-looking t/05notifier-loop.t result is a stale compatibility expectation: PerlOnJava now reports the native-Perl value of 2, but SkipUnsupportedSocketTests.patch changes that assertion to expect 3.
Proposed work
- Add a small project-owned regression test that reproduces the false extra
B::SV::REFCNT owner without requiring the full IO::Async suite. Validate it on system Perl first and on both PerlOnJava backends.
- Refine the B-specific
Internals::SvREFCNT(..., 1) heuristic so singly-owned IO::Async-style objects report 1 without breaking the DBIx::Class and thread lifecycle cases it was added to protect.
- Update
IO-Async-0.805/SkipUnsupportedSocketTests.patch so the final notifier-loop assertion expects the upstream/native count of 2.
- Rerun the focused Poll and Select timer, signal, idle, control, and metrics tests on both backends.
- Rerun the full IO::Async suite. Consider adding
TEST_QUICK_TIMERS=1 to the IO::Async distropref, or a justified runtime exception, so the random tester can finish within its global runtime policy. Keep this timeout adjustment separate from the correctness fix.
Acceptance criteria
- The new focused refcount regression passes under system Perl, JVM PerlOnJava, and interpreter PerlOnJava.
t/05notifier-loop.t and the affected Poll/Select loop tests pass on both PerlOnJava backends.
- Existing DBIx::Class and thread lifecycle/refcount regression coverage remains green.
- A bounded full
jcpan -t IO::Async run completes and passes with the maintained distropref configuration.
Summary
IO::Async0.805 regressed from PASS to FAIL in CPAN random-tester run20260826-133506-83222. The report is recorded as a 300-second timeout, but the suite had already completed several failing tests before the timeout. The failures are reproducible reference-count mismatches in both PerlOnJava backends.There are two related compatibility problems:
B::SV::REFCNTreports an extra owner for otherwise singly-ownedIO::Async::Loopobjects (expected 1, found 2).t/05notifier-loop.tstill expects a historical extra notifier owner (3), while current PerlOnJava reports2, which now matches system Perl.The overall 300-second cap is a secondary concern: IO::Async deliberately contains multi-second timer tests, and the archived run was still making progress. This is not evidence of a hang.
Distribution and configuration
PEVANS/IO-Async-0.805.tar.gzb5c01ce1cIO-Async/NoFork.patchIO-Async/PerlOnJava.patchIO-Async/SkipUnsupportedSocketTests.patchIO_ASYNC_NO_FORK=1Configuration and build both succeed. The failures occur during
Build test.Focused results
System Perl
Using the same IO::Async 0.805 source and required test dependencies:
t/05notifier-loop.t: PASS, 35/35t/11loop-poll-timer.t: PASS, 19/19The final native-Perl expectation in
t/05notifier-loop.tis 2 references.PerlOnJava JVM backend
t/05notifier-loop.t:t/11loop-poll-timer.t:PerlOnJava interpreter backend
Both focused tests produce the same failures and counts as the JVM backend. This points to shared runtime/refcount behavior rather than backend-specific compilation.
Other failures visible before timeout
The archived CPAN run also reports
expected 1, found 2in both Poll and Select variants of:The run reached tests after
t/22timer-absolute.tbefore the tester's 300-second total-runtime limit fired. Therefore the timeout classification masks real completed failures.Likely regression area
History and blame point to commit
fe9d6bc5f(feat: complete Perl ithread lifecycle compatibility, 2026-08-15). That commit changedB::SV::REFCNTto call:and added a special case in
Internals.svRefcount()that increments the reported count when:The special case was introduced to keep a real aggregate owner visible in DBIx::Class/thread lifecycle paths. It appears to misclassify the temporary/private owner created by
B::SVfor these IO::Async loop objects, producing 2 where native Perl and IO::Async expect 1.This regression point is inferred from the focused behavior and source history; it has not yet been confirmed by a commit-by-commit bisect.
The opposite-looking
t/05notifier-loop.tresult is a stale compatibility expectation: PerlOnJava now reports the native-Perl value of 2, butSkipUnsupportedSocketTests.patchchanges that assertion to expect 3.Proposed work
B::SV::REFCNTowner without requiring the full IO::Async suite. Validate it on system Perl first and on both PerlOnJava backends.Internals::SvREFCNT(..., 1)heuristic so singly-owned IO::Async-style objects report 1 without breaking the DBIx::Class and thread lifecycle cases it was added to protect.IO-Async-0.805/SkipUnsupportedSocketTests.patchso the final notifier-loop assertion expects the upstream/native count of 2.TEST_QUICK_TIMERS=1to the IO::Async distropref, or a justified runtime exception, so the random tester can finish within its global runtime policy. Keep this timeout adjustment separate from the correctness fix.Acceptance criteria
t/05notifier-loop.tand the affected Poll/Select loop tests pass on both PerlOnJava backends.jcpan -t IO::Asyncrun completes and passes with the maintained distropref configuration.