/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
stdlib/Serialization/test/runtests.jl
853 строки
24 KB
Keno Fischer
cancellation: Hook up libuv to cancellation (#62557)
05 авг 2026, 07:46
Не верифицирован
05 авг 2026, 07:46
35b7e12
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license using Test, Random, Serialization, Base64 using Base.ScopedValues: with @test isempty(Test.detect_closure_boxes(Serialization)) # Check that serializer hasn't gone out-of-frame @test Serialization.sertag(Symbol) == 1 @test Serialization.sertag(()) == 68 @test Serialization.sertag(false) == 76 function create_serialization_stream(f::Function) s = IOBuffer() f(s) close(s) end # Tags create_serialization_stream() do s Serialization.writetag(s, Serialization.sertag(Bool)) @test take!(s)[end] == UInt8(Serialization.sertag(Bool)) end create_serialization_stream() do s Serialization.write_as_tag(s, Serialization.sertag(Bool)) @test take!(s)[end] == UInt8(Serialization.sertag(Bool)) end create_serialization_stream() do s Serialization.write_as_tag(s, Serialization.sertag(Symbol)) data = take!(s) @test data[end-1] == 0x00 @test data[end] == UInt8(Serialization.sertag(Symbol)) end # SubString create_serialization_stream() do s ss = Any[SubString("12345", 2, 4), SubString(GenericString("12345"), 2, 4), SubString(s"12345", 2, 4), SubString(GenericString(s"12345"), 2, 4),] for x in ss serialize(s, x) end seek(s, 0) for x in ss y = deserialize(s) @test x == y @test typeof(x) == typeof(y) end end # Boolean & Empty & Nothing create_serialization_stream() do s serialize(s, true) serialize(s, ()) serialize(s, nothing) seek(s, 0) @test deserialize(s) === true @test deserialize(s) === () @test deserialize(s) === nothing end # Ptr create_serialization_stream() do s serialize(s, C_NULL) seekstart(s) @test deserialize(s) === C_NULL end # Integer create_serialization_stream() do s serialize(s, 0x1) serialize(s, 32) serialize(s, 33) seek(s,0) @test deserialize(s) === 0x01 @test deserialize(s) === 32 @test deserialize(s) === 33 end # Tuple create_serialization_stream() do s tpl = (0x1,'a') serialize(s, tpl) len = 257 lt = ntuple(i->0x1, len) serialize(s, lt) serialize(s, Tuple{}) seek(s, 0) @test deserialize(s) === tpl @test deserialize(s) === lt @test deserialize(s) === Tuple{} end # Dict create_serialization_stream() do s dct = Dict("A"=>1, "B"=>2) serialize(s, dct) seek(s, 0) @test deserialize(s) == dct end # Symbol create_serialization_stream() do s gensym(len) = Symbol(repeat("A", len)) smbl = gensym(1) serialize(s, smbl) smbl2 = gensym(10) serialize(s, smbl2) smbl3 = gensym(257) serialize(s, smbl3) seek(s, 0) @test deserialize(s) === smbl @test deserialize(s) === smbl2 @test deserialize(s) === smbl3 end # Module create_serialization_stream() do s # user-defined module mod = b"SomeModule" modstring = String(mod) eval(Meta.parse("module $(modstring); end")) modtype = eval(Meta.parse("$(modstring)")) serialize(s, modtype) seek(s, 0) @test invokelatest(deserialize, s) === modtype end # DataType create_serialization_stream() do s # standard types typ1 = UInt8 serialize(s, typ1) typ2 = Bool serialize(s, typ2) seek(s, 0) @test deserialize(s) == typ1 @test deserialize(s) == typ2 end create_serialization_stream() do s # user-defined type usertype = "SerializeSomeType" eval(Meta.parse("abstract type $(usertype) end")) utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) @test invokelatest(deserialize, s) === utype end create_serialization_stream() do s # user-defined type usertype = "SerializeSomeType1" eval(Meta.parse("mutable struct $(usertype); end")) utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) @test invokelatest(deserialize, s) === utype end create_serialization_stream() do s # user-defined type usertype = "SerializeSomeType2" eval(Meta.parse("abstract type $(usertype){T} end")) utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) @test invokelatest(deserialize, s) == utype end create_serialization_stream() do s # immutable struct with 1 field usertype = "SerializeSomeType3" eval(Meta.parse("struct $(usertype){T}; a::T; end")) utype = eval(Meta.parse("$(usertype)")) serialize(s, utype) seek(s, 0) @test invokelatest(deserialize, s) == utype end create_serialization_stream() do s # immutable struct with 2 fields usertype = "SerializeSomeType4" eval(Meta.parse("struct $(usertype){T}; a::T; b::T; end")) utval = eval(Meta.parse("$(usertype)(1,2)")) serialize(s, utval) seek(s, 0) @test invokelatest(deserialize, s) === utval end create_serialization_stream() do s # immutable struct with 3 fields usertype = "SerializeSomeType5" eval(Meta.parse("struct $(usertype){T}; a::T; b::T; c::T; end")) utval = eval(Meta.parse("$(usertype)(1,2,3)")) serialize(s, utval) seek(s, 0) @test invokelatest(deserialize, s) === utval end create_serialization_stream() do s # immutable struct with 4 fields usertype = "SerializeSomeType6" eval(Meta.parse("struct $(usertype){T}; a::T; b::T; c::T; d::T; end")) utval = eval(Meta.parse("$(usertype)(1,2,3,4)")) serialize(s, utval) seek(s, 0) @test invokelatest(deserialize, s) === utval end create_serialization_stream() do s # union types serialize(s, Union{Int,Float64}) serialize(s, Union{Int,Missing}) serialize(s, Union{Int,Float64,String}) serialize(s, [Union{Int,Float64}, Union{String,Symbol}, Union{Int,Missing}]) seek(s, 0) @test deserialize(s) === Union{Int,Float64} @test deserialize(s) === Union{Int,Missing} @test deserialize(s) === Union{Int,Float64,String} @test deserialize(s) == Type[Union{Int,Float64}, Union{String,Symbol}, Union{Int,Missing}] end # Expression create_serialization_stream() do s expr = Meta.parse("a = 1") serialize(s, expr) expr2 = Meta.parse(repeat("a = 1;", 300)) serialize(s, expr2) seek(s, 0) @test deserialize(s) == expr @test deserialize(s) == expr2 end # Array mutable struct TA1 v::UInt8 end create_serialization_stream() do s # small 1d array arr1 = fill(0x01, 10) # small 1d array serialize(s, arr1) arr2 = fill(0x01, 3, 3) # small 2d array serialize(s, arr2) arr3 = fill(0x0001, 50) # large 1d array serialize(s, arr3) arr4 = reshape([true, false, false, false, true, false, false, false, true], 3, 3) serialize(s, arr4) # boolean array arr5 = Vector{TA1}(undef, 3) arr5[2] = TA1(0x01) serialize(s, arr5) seek(s, 0) @test deserialize(s) == arr1 @test deserialize(s) == arr2 @test deserialize(s) == arr3 @test deserialize(s) == arr4 result = deserialize(s) @test !isassigned(result,1) @test !isassigned(result,3) @test result[2].v == arr5[2].v end # SubArray create_serialization_stream() do s # slices slc1 = view(UInt8[1,1,1,1], 2:3) serialize(s, slc1) slc2 = view(repeat(UInt8[1,2,3,4], 1, 4), 1, 2:4) serialize(s, slc2) seek(s, 0) @test deserialize(s) == slc1 @test deserialize(s) == slc2 end # Objects that have a SubArray as a type in a type-parameter list module ArrayWrappers struct ArrayWrapper{T,N,A<:AbstractArray} <: AbstractArray{T,N} data::A end ArrayWrapper(data::AbstractArray{T,N}) where {T,N} = ArrayWrapper{T,N,typeof(data)}(data) Base.size(A::ArrayWrapper) = size(A.data) Base.size(A::ArrayWrapper, d) = size(A.data, d) Base.getindex(A::ArrayWrapper, i::Real...) = getindex(A.data, i...) end let A = rand(3,4) for B in (view(A, :, 2:4), view(A, 2, 1:3)) C = ArrayWrappers.ArrayWrapper(B) io = IOBuffer() serialize(io, C) seek(io, 0) Cd = deserialize(io) @test size(Cd) == size(C) @test Cd == B end end # Function module DefinitelyNotMain serialize_test_function() = 1 serialize_test_function2 = ()->1 end serialize_test_function = DefinitelyNotMain.serialize_test_function serialize_test_function2 = DefinitelyNotMain.serialize_test_function2 create_serialization_stream() do s # Base generic function serialize(s, sin) serialize(s, typeof) serialize(s, serialize_test_function) serialize(s, serialize_test_function2) seek(s, 0) @test deserialize(s) === sin @test deserialize(s) === typeof f1 = deserialize(s) f2 = deserialize(s) @test first(methods(f1)).module === DefinitelyNotMain @test first(methods(f2)).module === DefinitelyNotMain @test f1() === 1 @test f2() === 1 end # Anonymous Functions main_ex = quote using Serialization $create_serialization_stream() do s local g() = :magic_token_anon_fun_test local gkw(; kw=:thekw) = kw serialize(s, g) serialize(s, g) serialize(s, gkw) seekstart(s) ds = Serializer(s) local g2 = deserialize(ds) @test g2 !== g $Test.@test Base.invokelatest(g2) === :magic_token_anon_fun_test $Test.@test Base.invokelatest(g2) === :magic_token_anon_fun_test deserialize(ds) === g2 local gkw2 = deserialize(s) $Test.@test gkw2 !== gkw $Test.@test Base.invokelatest(gkw2) === :thekw $Test.@test Base.invokelatest(gkw2, kw="kwtest") === "kwtest" # issue #21793 y = x -> (() -> x) seekstart(s) serialize(s, y) seekstart(s) y2 = deserialize(s) $Test.@test Base.invokelatest() do x2 = y2(2) x2() end === 2 end end # This needs to be run on `Main` since the serializer treats it differently. Core.eval(Main, main_ex) # Task # Runnable tasks drop CodeInstance hints but preserve explicit invoke targets. serialization_task_result() = 42 create_serialization_stream() do s t = Task(serialization_task_result) serialize(s, t) seek(s, 0) r = deserialize(s) @test fetch(schedule(r)) === 42 @test fetch(schedule(t)) === 42 end serialization_task_invoke() = 43 serialization_task_invoke(x...) = x function serialization_task_with_invoke(@nospecialize(f), @nospecialize(invoked)) t = Core._task(f, 0, invoked) t.donenotify = Base.ThreadSynchronizer() return t end for invoked in (Tuple{Vararg}, which(serialization_task_invoke, (Vararg,))) create_serialization_stream() do s t = serialization_task_with_invoke(serialization_task_invoke, invoked) serialize(s, t) seek(s, 0) r = deserialize(s) @test fetch(schedule(r)) === () @test fetch(schedule(t)) === () end end create_serialization_stream() do s # user-defined type array f = () -> begin task_local_storage(:v, 2); return 1+1 end t = Task(f) Base.wait(schedule(t)) serialize(s, t) seek(s, 0) r = deserialize(s) @test r.storage[:v] == 2 @test r.state === :done @test r.exception === nothing end struct MyErrorTypeTest <: Exception end create_serialization_stream() do s # user-defined type array t = Task(()->throw(MyErrorTypeTest())) @test_throws TaskFailedException(t) Base.wait(schedule(t)) @test isa(t.exception, MyErrorTypeTest) serialize(s, t) seek(s, 0) r = deserialize(s) @test r.state === :failed end # corner case: undefined inside immutable struct struct MyNullable{T} hasvalue::Bool value::T MyNullable{T}() where {T} = new(false) end create_serialization_stream() do s serialize(s, MyNullable{Any}()) seekstart(s) n = deserialize(s) @test isa(n, MyNullable) @test !isdefined(n, :value) end # cycles module CycleFoo echo(x)=x afunc = (x)->x end echo(x) = x create_serialization_stream() do s A = Any[1,2,3,abs,abs,CycleFoo.afunc,CycleFoo.afunc,echo,echo,CycleFoo.echo,CycleFoo.echo,4,5] A[3] = A serialize(s, A) seekstart(s) b = deserialize(s) @test b[3] === b @test b[1] == 1 @test b[4] === abs @test b[5] === b[4] @test b[5](-1) == 1 @test b[6] === b[7] @test b[6]("Hello") == "Hello" @test b[8] === b[9] @test b[8]("World") == "World" @test b[10] === b[11] @test b[10]("foobar") == "foobar" @test b[end] == 5 @test length(b) == length(A) @test isa(b,Vector{Any}) end # shared references create_serialization_stream() do s A = [1,2] B = [A,A] serialize(s, B) seekstart(s) C = deserialize(s) @test C == B @test C[1] === C[2] end mutable struct MSingle end create_serialization_stream() do s x = MSingle() A = [x, x, MSingle()] serialize(s, A) seekstart(s) C = deserialize(s) @test A[1] === x === A[2] !== A[3] @test x !== C[1] === C[2] !== C[3] end # Regex create_serialization_stream() do s r1 = r"a?b.*" serialize(s, r1) seekstart(s) r2 = deserialize(s) @test r1 == r2 end # issue #13452 module Test13452 using Test, Serialization module Shell export foo foo(x) = error("Instances must implement foo") end module Instance1 using ..Shell Shell.foo(x::Int) = "This is an Int" end using .Shell, .Instance1 io = IOBuffer() serialize(io, foo) str = String(take!(io)) @test !occursin("Instance1", str) @test occursin("Shell", str) end # module Test13452 # issue #15163 mutable struct B15163{T} x::Array{T} end let b = IOBuffer() serialize(b,B15163([1])) seekstart(b) c = deserialize(b) @test isa(c,B15163) && c.x == [1] end # related issue #20066 let b = IOBuffer() serialize(b, Dict{Vector, Vector}()) seekstart(b) c = deserialize(b) @test isa(c, Dict{Vector, Vector}) end # issue #15849 let b = IOBuffer() vt = Tuple[(1,)] serialize(b, vt) seekstart(b) @test deserialize(b) == vt end # issue #1770 let a = ['T', 'e', 's', 't'], f = IOBuffer() # issue #1770 serialize(f, a) seek(f, 0) @test deserialize(f) == a f = IOBuffer() serialize(f, a) seek(f, 0) @test deserialize(f) == a # issue #4414 seek(f,0) serialize(f, :β) seek(f,0) @test deserialize(f) === :β end # issue #20324 struct T20324{T} x::T end let x = T20324[T20324(1) for i = 1:2] b = IOBuffer() serialize(b, x) seekstart(b) y = deserialize(b) @test isa(y,Vector{T20324}) @test y == x end @testset "serializer header" begin io = IOBuffer() serialize(io, ()) seekstart(io) b = read(io) @test b[1] == Serialization.HEADER_TAG @test b[2:3] == b"JL" @test b[4] == Serialization.ser_version @test (b[5] & 0x3) == (ENDIAN_BOM == 0x01020304) @test ((b[5] & 0xc)>>2) == (sizeof(Int) == 8) @test (b[5] & 0xf0) == 0 @test all(b[6:8] .== 0) # Detection of incompatible binary serializations function corrupt_header(bytes, offset, val) b = copy(bytes) b[offset] = val IOBuffer(b) end @test_throws( ErrorException("""Cannot read stream serialized with a newer version of Julia. Got data version 255 > current version $(Serialization.ser_version)"""), deserialize(corrupt_header(b, 4, 0xff))) @test_throws(ErrorException("Unknown word size flag in header"), deserialize(corrupt_header(b, 5, 2<<2))) @test_throws(ErrorException("Unknown endianness flag in header"), deserialize(corrupt_header(b, 5, 2))) other_wordsize = sizeof(Int) == 8 ? 4 : 8 other_endianness = bswap(ENDIAN_BOM) @test_throws(ErrorException("Serialized byte order mismatch ($(repr(other_endianness)))"), deserialize(corrupt_header(b, 5, UInt8(ENDIAN_BOM != 0x01020304)))) end # issue #26979 let io = IOBuffer() function gen_f(a::T) where T f = x -> T(x) return f end f = gen_f(1f0) serialize(io, f) seekstart(io) f2 = deserialize(io) @test invokelatest(f2, 1) === 1f0 end # using a filename; #30151 let f = tempname(), x = [rand(2,2), :x, "hello"] serialize(f, x) @test deserialize(f) == x rm(f) end let f_data # a serialized function from v1.0/v1.1 if Int === Int64 f_data = "N0pMBwQAAAA0MxMAAAAAAAAAAAEFIyM1IzYiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjNRUAI+AjAQAAAAAAAAAfTgEETWFpbkQBAiM1AQdSRVBMWzNdvxBTH04BBE1haW6bRAMAAAAzLAAARkYiAAAAAE7BTBsVRuIWA1YkH04BBE1haW5EAQEq4SXhFgNWJB9OAQRNYWluRJ0o4CXiFgFVKOEVAAbiAQAAAAEAAAABAAAATuIVRuA0EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRB9OAQRNYWluRAECIzUBB1JFUExbM13g3xXfFeIAAAAVRuKifX5MTExMTuIp" else f_data = "N0pMBwAAAAA0MxMAAAAAAAAAAAEFIyM1IzYiAAAAABBYH04BBE1haW6bRCIAAAAAIgAAAABNTEy+AQIjNRUAI78jAQAAAAAAAAAfTgEETWFpbkQBAiM1AQdSRVBMWzJdvxBTH04BBE1haW6bRAMAAAAzLAAARkYiAAAAAE7BTBsVRsEWA1YkH04BBE1haW5EAQEqwCXAFgNWJB9OAQRNYWluRJ0ovyXBFgFVKMAVAAbBAQAAAAEAAAABAAAATsEVRr80EAEMTGluZUluZm9Ob2RlH04BBE1haW6bRB9OAQRNYWluRAECIzUBB1JFUExbMl2/vhW+FcEAAAAVRsGifX5MTExMTsEp" end f = deserialize(IOBuffer(base64decode(f_data))) @test invokelatest(f, 10,3) == 23 end # issue #33466, IdDict let d = IdDict([1] => 2, [3] => 4), io = IOBuffer() serialize(io, d) seekstart(io) ds = deserialize(io) @test Dict(d) == Dict(ds) @test all([k in keys(ds) for k in keys(ds)]) end # issue #35030, shared references to Strings let s = join(rand('a':'z', 1024)), io = IOBuffer() serialize(io, (s, s)) seekstart(io) s2 = deserialize(io) @test Base.summarysize(s2) < 2*sizeof(s) end # issue #39895 @eval Main begin using Test, Serialization let g = gensym(:g) closure = eval(:(f -> $g(x) = f(x))) inc(x) = x + 1 b = IOBuffer() serialize(b, closure(inc)) seekstart(b) f = deserialize(b) # this should not crash @test_broken f(1) == 2 end end let c1 = Threads.Condition() c2 = Threads.Condition(c1.lock) lock(c2) t = @task nothing Base.schedule_on_notify!(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 @testset "LazyString" begin l1 = lazy"a $1 b $2" l2 = deserialize(IOBuffer(sprint(serialize, l1))) @test l2.str === l1.str @test l2 == l1 @test l2.parts === () end @testset "Core.IntrinsicFunction" begin create_serialization_stream() do s serialize(s, Core.Intrinsics.add_int) serialize(s, Core.Intrinsics.xor_int) serialize(s, Core.Intrinsics.sub_float) seekstart(s) @test deserialize(s) === Core.Intrinsics.add_int @test deserialize(s) === Core.Intrinsics.xor_int @test deserialize(s) === Core.Intrinsics.sub_float end end @testset "Docstrings" begin undoc = Docs.undocumented_names(Serialization) @test_broken isempty(undoc) @test undoc == [:AbstractSerializer, :Serializer] end # test method definitions from v1.11 if Int === Int64 let f_data = "N0pMGgQAAAAWAQEFdGh1bmsbFUbnFgEBBXRodW5rGxVG4DoWAQEGbWV0aG9kAQtmMTExX3RvXzExMhUABuABAAAA4BUAB+AAAAAAThVG4DQQAQxMaW5lSW5mb05vZGUfTptEH04BBE1haW5EAQ90b3AtbGV2ZWwgc2NvcGUBBG5vbmW+vhUAAd8V305GTk4JAQAAAAAAAAAJ//////////9MTExMAwADAAUAAAX//xYBAQZtZXRob2QsBwAWAlYkH06bRAEGVHlwZW9mLAcAFgNWJB9Om0QBBHN2ZWMo4iQfTptETxYBViQfTptEAQRzdmVjFgRWJB9Om0QBBHN2ZWMo4yjkGhfgAQRub25lFgMBBm1ldGhvZCwHACjlGxVG5AEBXhYDViQfTptElyQfTp5EAQNWYWzhFgFWKOEWBFYkH06eRAELbGl0ZXJhbF9wb3co4CXhKOI6KOMVAAbkAQAAAAEAAAABAAAAAQAAAAAAAADkFQAH5AAAAAAAAAAAAAAAAAAAAAAAAAAAThVG4DQsCwAfTgEETWFpbkQBBG5vbmUBBG5vbmW/vhUAAeGifRXhAAhORk5OCQEAAAAAAAAACf//////////TExMTAMAAwAFAAAF//86ThUABucBAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAAAAAOcVAAfnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOFUbgNCwLAB9OAQRNYWluRCwNAAEEbm9uZb6+FQAB3xXfTkZOTgkBAAAAAAAAAAn//////////0xMTEwDAAMABQAABf//" @eval Main function f111_to_112 end Core.eval(Main, with(Serialization.current_module => Main) do deserialize(IOBuffer(base64decode(f_data))) end) @test @invokelatest(Main.f111_to_112(16)) == 256 end end @testset "MemoryRef" begin old_m = Memory{Int}(undef, 10) for i in 1:10 old_m[i] = i^2 end # Test roundtrip at every offset for idx in 1:10 old_x = memoryref(old_m, idx) @test old_x[] == idx^2 old_d = Dict(:x => old_x) old_str = sprint(serialize, old_d) new_d = deserialize(IOBuffer(old_str)) @test new_d[:x] isa MemoryRef @test new_d[:x][] == idx^2 end end @testset "Memory" begin old_m = Memory{Int}(undef, 10) for i in 1:10 old_m[i] = i^3 end @test old_m[5] == 125 old_d = Dict(:m => old_m) old_str = sprint(serialize, old_d) new_d = deserialize(IOBuffer(old_str)) @test new_d[:m] isa Memory @test new_d[:m][5] == 125 end @testset "CancellationTokenSource" begin using Base: cancel!, CancellationToken, CancellationTokenSource # a diamond: identity sharing of parents must survive the round-trip root = CancellationTokenSource() left = CancellationTokenSource(CancellationToken(root)) right = CancellationTokenSource(CancellationToken(root)) child = CancellationTokenSource(CancellationToken(left), CancellationToken(right)) buf = IOBuffer() serialize(buf, (root, left, right, child)) seekstart(buf) r2, l2, rt2, c2 = deserialize(buf) @test r2 isa CancellationTokenSource && c2.nparents == 2 @test Base._cancel_parent(c2, 1) === l2 && Base._cancel_parent(c2, 2) === rt2 @test Base._cancel_parent(l2, 1) === r2 && Base._cancel_parent(rt2, 1) === r2 @test !Base.iscancelled(c2) # the deserialized graph is relinked: cancelling its root reaches the leaf cancel!(r2) @test Base.iscancelled(c2) && Base.iscancelled(l2) && Base.iscancelled(rt2) # ...without affecting the original graph @test !Base.iscancelled(root) && !Base.iscancelled(child) # cancellation state round-trips src = CancellationTokenSource() cancel!(src, Base.CANCEL_REQUEST_ABANDON_EXTERNAL) buf = IOBuffer() serialize(buf, src) seekstart(buf) s2 = deserialize(buf) @test Base.cancel_severity(s2) === Base.CANCEL_REQUEST_ABANDON_EXTERNAL # the copy is independent: cancelling the original after serialization # does not affect the deserialized graph p = CancellationTokenSource() c = CancellationTokenSource(CancellationToken(p)) buf = IOBuffer() serialize(buf, (p, c)) seekstart(buf) cancel!(p) # does not affect the serialized bytes p3, c3 = deserialize(buf) @test !Base.iscancelled(c3) # p3 was not cancelled at relink time # deserializing under an already-cancelled parent is born cancelled: # a stream may legally record a cancelled parent with an uncancelled # child (e.g. the serializer raced an in-flight cancel! walk); the # child inherits the state when it relinks under the parent p = CancellationTokenSource() c = CancellationTokenSource(CancellationToken(p)) Base._raise_state!(p, 0x01) # cancel p without walking to c @test !Base.iscancelled(c) buf = IOBuffer() serialize(buf, c) # parents serialize first, with their state seekstart(buf) c4 = deserialize(buf) @test Base.iscancelled(Base._cancel_parent(c4, 1)) @test Base.iscancelled(c4) # a corrupt stream cannot inject an out-of-range severity src = CancellationTokenSource() buf = IOBuffer() serialize(buf, src) data = take!(buf) data[end] = 0xbf # state byte: invalid severity 0xbf @test_throws ArgumentError deserialize(IOBuffer(data)) end @testset "WaitEntryN" begin # variable-sized runtime object: it must be reconstructed through the # runtime allocator (the generic path would allocate only the fixed # datatype size, and the GC would then scan a nonexistent slot tail) w = Base.WaitEntryN(current_task(), 4) buf = IOBuffer() serialize(buf, w) seekstart(buf) w2 = deserialize(buf) @test w2 isa Core.WaitEntryN @test Base._nslots(w2) == 4 # transient wait state does not round-trip: no task, fresh free slots @test (@atomic :monotonic w2.task) === nothing @test all(i -> Base._slot_owner(w2, i) === nothing, 1:4) GC.gc(true) # identity sharing within one stream buf = IOBuffer() serialize(buf, (w, w)) seekstart(buf) a, b = deserialize(buf) @test a === b && Base._nslots(a) == 4 GC.gc(true) end