From 9b87ee92bb851e904e0b5d3127396157e41aabb4 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 17 Dec 2021 18:50:46 -0500 Subject: [PATCH] [Serialization] support AbstractLock and GenericCondition (#43325) Locks should be unlocked when serialized, and GenericCondition should drop their waiters. Otherwise, we may try to serialize a running Task (the user should normally be holding the lock around the data they are intending to serialize), which will fail and error. --- base/deepcopy.jl | 18 ++++++++++++++++ stdlib/Serialization/src/Serialization.jl | 26 +++++++++++++++++++++++ stdlib/Serialization/test/runtests.jl | 18 ++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/base/deepcopy.jl b/base/deepcopy.jl index 7ea5a041fc6327..317d999004c42f 100644 --- a/base/deepcopy.jl +++ b/base/deepcopy.jl @@ -126,3 +126,21 @@ function deepcopy_internal(x::Union{Dict,IdDict}, stackdict::IdDict) end dest end + +function deepcopy_internal(x::AbstractLock, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x] + end + y = typeof(x)() + stackdict[x] = y + return y +end + +function deepcopy_internal(x::GenericCondition, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x] + end + y = typeof(x)(deepcopy_internal(x.lock)) + stackdict[x] = y + return y +end diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index 9a4ebc7af8c2ac..7df53f216716f6 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -1528,4 +1528,30 @@ function deserialize(s::AbstractSerializer, ::Type{Base.StackTraces.StackFrame}) return Base.StackTraces.StackFrame(func, file, line, nothing, from_c, inlined, pointer) end +function serialize(s::AbstractSerializer, lock::Base.AbstractLock) + # assert_havelock(lock) + serialize_cycle_header(s, lock) + nothing +end + +function deserialize(s::AbstractSerializer, ::Type{T}) where T<:Base.AbstractLock + lock = T() + deserialize_cycle(s, lock) + return lock +end + +function serialize(s::AbstractSerializer, cond::Base.GenericCondition) + serialize_cycle_header(s, cond) && return + serialize(s, cond.lock) + nothing +end + +function deserialize(s::AbstractSerializer, ::Type{T}) where T<:Base.GenericCondition + lock = deserialize(s) + cond = T(lock) + deserialize_cycle(s, cond) + return cond +end + + end diff --git a/stdlib/Serialization/test/runtests.jl b/stdlib/Serialization/test/runtests.jl index 18ce00055ad0b6..ceacb7f33c27af 100644 --- a/stdlib/Serialization/test/runtests.jl +++ b/stdlib/Serialization/test/runtests.jl @@ -624,3 +624,21 @@ end @test_broken f(1) == 2 end end + +let c1 = Threads.Condition() + c2 = Threads.Condition(c1.lock) + lock(c2) + t = @task nothing + Base._wait2(c1, t) + c3, c4 = deserialize(IOBuffer(sprint(serialize, [c1, c2])))::Vector{Threads.Condition} + @test c3.lock === c4.lock + @test islocked(c1) + @test !islocked(c3) + @test !isempty(c1.waitq) + @test isempty(c2.waitq) + @test isempty(c3.waitq) + @test isempty(c4.waitq) + notify(c1) + unlock(c2) + wait(t) +end