From 86fae9d32b0465f21312cdbfd3e477011aff7465 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Thu, 19 Aug 2021 10:46:12 -0400 Subject: [PATCH] Add test for concurrent compile() and TimeZone construction. Add comments --- src/types/timezone.jl | 4 +++ test/thread-safety.jl | 68 ++++++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/types/timezone.jl b/src/types/timezone.jl index 01eb5fb07..25d36c363 100644 --- a/src/types/timezone.jl +++ b/src/types/timezone.jl @@ -1,3 +1,7 @@ +# Thread-local TimeZone cache, which caches time zones _per thread_, allowing thread-safe +# caching. Note that this means the cache will grow in size, and may store redundant objects +# accross multiple threads, but this extra space usage allows for fast, lock-free access +# to the cache, while still being thread-safe. const THREAD_TZ_CACHES = Dict{String,Tuple{TimeZone,Class}}[] # Based upon the thread-safe Global RNG implementation in the Random stdlib: diff --git a/test/thread-safety.jl b/test/thread-safety.jl index c945075aa..b2028e590 100644 --- a/test/thread-safety.jl +++ b/test/thread-safety.jl @@ -10,39 +10,55 @@ using Test @assert Threads.nthreads() > 1 "This system does not support multiple threads, so the thread-safety tests cannot be run." -function create_zdt(year, month, day, tz_name) - ZonedDateTime(DateTime(year, month, day), TimeZone(tz_name)) -end -function cycle_zdts() - return [ - try - create_zdt(year, month, day, tz_name) - catch e - e isa Union{ArgumentError,NonExistentTimeError} || rethrow() - nothing +@testset "Multithreaded TimeZone brute force test" begin + function create_zdt(year, month, day, tz_name) + ZonedDateTime(DateTime(year, month, day), TimeZone(tz_name)) + end + function cycle_zdts() + return [ + try + create_zdt(year, month, day, tz_name) + catch e + # Ignore ZonedDateTimes that aren't valid + e isa Union{ArgumentError,AmbiguousTimeError,NonExistentTimeError} || rethrow() + nothing + end + for year in 2000:2020 + for month in 1:5 + for day in 10:15 + for tz_name in timezone_names() + ] + end + + outputs = Channel(Inf) + @sync begin + for _ in 1:15 + Threads.@spawn begin + put!(outputs, cycle_zdts()) + end end - for year in 2000:2020 - for month in 1:5 - for day in 10:15 - for tz_name in timezone_names() - ] + end + close(outputs) + + tzs = collect(outputs) + + # Test that every Task produced the same result + allsame(x) = all(y -> y == first(x), x) + @test allsame(tzs) end -const outputs = Channel(Inf) -@sync begin - for _ in 1:15 +#---------------------------------------------------- + +@testset "Interleaved compile() and TimeZone construction" begin + @sync for i in 1:20 + if (i % 5 == 0) + TimeZones.TZData.compile() + end Threads.@spawn begin - put!(outputs, cycle_zdts()) + TimeZone("US/Eastern", TimeZones.Class(:LEGACY)) end end end -close(outputs) - -const tzs = collect(outputs) - -# Test that every Task produced the same result -allsame(x) = all(y -> y == first(x), x) -@test allsame(tzs) """ @info "Running Thread Safety tests"