From 51ad797b50ba67294ecc2b7942173699addd5a9f Mon Sep 17 00:00:00 2001 From: lkdvos Date: Fri, 2 Oct 2026 15:41:33 -0400 Subject: [PATCH] Fix collect on caches that other tasks modify `collect` sized its result from `length(c)` and then iterated a snapshot taken separately, so it threw when another task changed the cache in between. Caches now report an unknown iterator size. Co-Authored-By: Claude Opus 5.5 --- src/containers/interface.jl | 2 ++ test/test_containers.jl | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/containers/interface.jl b/src/containers/interface.jl index f37f4d6..0ad2532 100644 --- a/src/containers/interface.jl +++ b/src/containers/interface.jl @@ -133,6 +133,8 @@ Base.isempty(c::AbstractCache) = @lock c.lock isempty(c.index) # Iteration walks a snapshot copied under the lock, so it is safe while other tasks use the # cache (holding the lock across `iterate` calls would deadlock on an early `break`). +# The length may change between `length` and `iterate`, so `collect` must not rely on it. +Base.IteratorSize(::Type{<:AbstractCache}) = Base.SizeUnknown() function Base.iterate(c::AbstractCache) snapshot = @lock c.lock _pairs(c) return iterate(c, (snapshot, 1)) diff --git a/test/test_containers.jl b/test/test_containers.jl index e83b854..99d9788 100644 --- a/test/test_containers.jl +++ b/test/test_containers.jl @@ -105,6 +105,24 @@ end @test all(((k, v),) -> v == 3k, c) end +@testset "$C: collect while other tasks write" for C in CACHETYPES + c = C{Int, Int}(; maxsize = 50) + done = Threads.Atomic{Bool}(false) + writer = Threads.@spawn while !done[] + foreach(k -> get!(() -> k, c, k), 1:200) + empty!(c) + yield() + end + ok = true + for _ in 1:2_000 + ok &= all(((k, v),) -> k == v, collect(c)) # used to throw when the length changed + yield() + end + done[] = true + wait(writer) + @test ok +end + @testset "$C: freed slots release their values" for C in CACHETYPES c = C{Int, Base.RefValue{Int}}(; maxsize = 3) # build the value inside a function so no local keeps it alive