Skip to content

Flaky test: WEAK-KEY-AND-VALUE-WEAKNESS fails intermittently on ubuntu-latest/bytecode #1814

Description

@dg1sbg

WEAK-KEY-AND-VALUE-WEAKNESS (src/lisp/regression-tests/hash-tables0.lisp:147) fails intermittently in CI. Same commit, same job, opposite results:

run commit clasp/ubuntu-latest/bytecode/no/no
clasp-developers/clasp 30693073178 661438586 "Run regression tests" failedFailures: WEAK-KEY-AND-VALUE-WEAKNESS, Successes: 1967
dg1sbg/clasp 30697205345 661438586 (identical) "Run regression tests" passed — Successes: 1968

(The fork run's job still shows red, but for an unrelated reason: the runner received a shutdown signal during the later ANSI step. Its regression-test step succeeded.)

Only this OS/mode combination is affected. In the upstream run above, clasp/macos-latest/bytecode and clasp/ubuntu-latest/native both passed with the same commit.

Why it is fragile

(test weak-key-and-value-weakness
      (let ((table (make-hash-table :weakness :key-and-value)))
        (setf (gethash (list nil) table) :value
              (gethash :key table) (list nil)
              (gethash (list nil) table) (list nil)
              (gethash :key table) :value)
        (gctools:garbage-collect)
        (hash-table-count table))
      (1))

The test allocates three throwaway (list nil) values, triggers one GC, and asserts the count is exactly 1. Under Boehm's conservative stack scanning, a single stale word in a register or stack slot still referencing one of those temporaries keeps its entry alive and the count comes out 2. Whether that happens depends on register allocation and stack residue at the call site, so it is sensitive to unrelated codegen changes and to the state left behind by the ~20 suites that run before hash-tables0.

weak-key-weakness immediately above it has the same shape and presumably the same exposure.

Not reproducible locally

macOS arm64, both variants, all clean:

  • 200/200 iterations of the test body in a fresh image
  • 5/5 runs of TEST_SUITES=fastgf,clos,mop,hash-tables0
  • full suites: boehm 1977, boehmprecise 1979, zero unexpected failures, including a from-scratch rebuild

Suggestion

Rather than asserting an exact count after one GC, either assert the surviving entry is present ((gethash :key table):value) and treat extra entries as tolerable, or retry the collect a bounded number of times before asserting. As written the test can redden any PR at random, which makes red CI easy to dismiss — the failure mode you least want on a project where CI is the stability gate.

Found while working on #1811 / #1812; unrelated to that change, which touches only CLOS class-identity guards and creates no new retention path (verified: the eql-specializer intern table is unchanged by hash-table use, and weak tables still collapse to 1 after 500 EQL dispatches with fully populated call histories).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions