Files
Nim/tests/gc/weakrefs.nim
metagn 660a9cecf0 add retries to testament, use it for GC tests (#24279)
Testament now retries a test by a specified amount if it fails in any
way other than an invalid spec. This is to deal with the flaky GC tests
on Windows CI that fail in many different ways, from the linker randomly
erroring, segfaults, etc.

Unfortunately I couldn't do this cleanly in testament's current code.
The proc `addResult`, which is the "final" proc called in a test run's
lifetime, is now wrapped in a proc `finishTest` that returns a bool
`true` if the test failed and has to be retried. This result is
propagated up from `cmpMsgs` and `compilerOutputTests` until it reaches
`testSpecHelper`, which handles these results by recursing if the test
has to be retried. Since calling `testSpecHelper` means "run this test
with one given configuration", this means every single matrix
option/target etc. receive an equal amount of retries each.

The result of `finishTest` is ignored in cases where it's known that it
won't be retried due to passing, being skipped, having an invalid spec
etc. It's also ignored in `testNimblePackages` because it's not
necessary for those specific tests yet and similar retry behavior is
already implemented for part of it.

This was a last resort for the flaky GC tests but they've been a problem
for years at this point, they give us more work to do and turn off
contributors. Ideally GC tests failing should mark as "needs review" in
the CI rather than "failed" but I don't know if Github supports
something like this.

(cherry picked from commit 720d0aee5c)
2025-01-14 07:35:50 +01:00

59 lines
1.1 KiB
Nim

discard """
output: "true"
retries: 2
"""
import intsets
type
TMyObject = object
id: int
StrongObject = ref TMyObject
WeakObject = object
id: int
data: ptr TMyObject
var
gid: int # for id generation
valid = initIntSet()
proc finalizer(x: StrongObject) =
valid.excl(x.id)
when defined(gcDestructors):
proc `=destroy`(x: var TMyObject) =
valid.excl(x.id)
proc create: StrongObject =
when defined(gcDestructors):
new(result)
else:
new(result, finalizer)
result.id = gid
valid.incl(gid)
inc gid
proc register(s: StrongObject): WeakObject =
result.data = cast[ptr TMyObject](s)
result.id = s.id
proc access(w: WeakObject): StrongObject =
## returns nil if the object doesn't exist anymore
if valid.contains(w.id):
result = cast[StrongObject](w.data)
proc main =
var s: seq[WeakObject]
newSeq(s, 10_000)
for i in 0 .. s.high:
s[i] = register(create())
# test that we have at least 80% unreachable weak objects by now:
when defined(gcMarkAndSweep):
GC_fullcollect()
var unreachable = 0
for i in 0 .. s.high:
if access(s[i]) == nil: inc unreachable
echo unreachable > 8_000
main()