Skip to content

Commit 6b41538

Browse files
authored
Merge pull request #17619 from JuliaLang/jn/ser-typename
ensure known_object_data is assigned before deserialize is called again
2 parents e70de21 + d584142 commit 6b41538

File tree

5 files changed

+119
-94
lines changed

5 files changed

+119
-94
lines changed

base/clusterserialize.jl

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,32 @@
11
# This file is a part of Julia. License is MIT: http://julialang.org/license
22

33
import .Serializer: known_object_data, object_number, serialize_cycle, deserialize_cycle, writetag,
4-
__deserialized_types__, serialize_typename_body, deserialize_typename_body,
4+
__deserialized_types__, serialize_typename, deserialize_typename,
55
TYPENAME_TAG, object_numbers
66

77
type ClusterSerializer{I<:IO} <: AbstractSerializer
88
io::I
99
counter::Int
1010
table::ObjectIdDict
1111

12-
sent_objects::Dict{UInt64, Bool} # used by serialize (track objects sent)
12+
sent_objects::Set{UInt64} # used by serialize (track objects sent)
1313

14-
ClusterSerializer(io::I) = new(io, 0, ObjectIdDict(), Dict())
14+
ClusterSerializer(io::I) = new(io, 0, ObjectIdDict(), Set{UInt64}())
1515
end
1616
ClusterSerializer(io::IO) = ClusterSerializer{typeof(io)}(io)
1717

1818
function deserialize(s::ClusterSerializer, ::Type{TypeName})
19-
number, full_body_sent = deserialize(s)
20-
makenew = false
21-
known = haskey(known_object_data, number)
19+
full_body_sent = deserialize(s)
20+
number = read(s.io, UInt64)
2221
if !full_body_sent
23-
if !known
24-
error("Expected object in cache. Not found.")
25-
else
26-
tn = known_object_data[number]::TypeName
22+
tn = get(known_object_data, number, nothing)::TypeName
23+
if !haskey(object_numbers, tn)
24+
# setup reverse mapping for serialize
25+
object_numbers[tn] = number
2726
end
27+
deserialize_cycle(s, tn)
2828
else
29-
name = deserialize(s)
30-
mod = deserialize(s)
31-
if known
32-
tn = known_object_data[number]::TypeName
33-
elseif mod !== __deserialized_types__ && isdefined(mod, name)
34-
tn = getfield(mod, name).name
35-
# TODO: confirm somehow that the types match
36-
#warn(mod, ".", name, " isdefined, need not have been serialized")
37-
name = tn.name
38-
mod = tn.module
39-
else
40-
name = gensym()
41-
mod = __deserialized_types__
42-
tn = ccall(:jl_new_typename_in, Ref{TypeName}, (Any, Any), name, mod)
43-
makenew = true
44-
end
45-
end
46-
deserialize_cycle(s, tn)
47-
full_body_sent && deserialize_typename_body(s, tn, number, name, mod, makenew)
48-
!known && (known_object_data[number] = tn)
49-
if !haskey(object_numbers, tn)
50-
object_numbers[tn] = number
29+
tn = deserialize_typename(s, number)
5130
end
5231
return tn
5332
end
@@ -57,15 +36,13 @@ function serialize(s::ClusterSerializer, t::TypeName)
5736
writetag(s.io, TYPENAME_TAG)
5837

5938
identifier = object_number(t)
60-
if !haskey(s.sent_objects, identifier)
61-
serialize(s, (identifier, true))
62-
serialize(s, t.name)
63-
serialize(s, t.module)
64-
serialize_typename_body(s, t)
65-
s.sent_objects[identifier] = true
66-
# println(t.module, ":", t.name, ", id:", identifier, " sent")
67-
else
68-
serialize(s, (identifier, false))
69-
# println(t.module, ":", t.name, ", id:", identifier, " NOT sent")
39+
send_whole = !(identifier in s.sent_objects)
40+
serialize(s, send_whole)
41+
write(s.io, identifier)
42+
if send_whole
43+
serialize_typename(s, t)
44+
push!(s.sent_objects, identifier)
7045
end
46+
# println(t.module, ":", t.name, ", id:", identifier, send_whole ? " sent" : " NOT sent")
47+
nothing
7148
end

base/methodshow.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ function show(io::IO, m::Method; kwtype::Nullable{DataType}=Nullable{DataType}()
7979
decls = Any[(), ("...", "")]
8080
elseif ft <: Function &&
8181
isdefined(ft.name.module, ft.name.mt.name) &&
82+
# TODO: more accurate test? (tn.name === "#" name)
8283
ft == typeof(getfield(ft.name.module, ft.name.mt.name))
8384
print(io, ft.name.mt.name)
8485
elseif isa(ft, DataType) && is(ft.name, Type.name) && isleaftype(ft)

base/serialize.jl

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ end
374374
function serialize(s::AbstractSerializer, g::GlobalRef)
375375
writetag(s.io, GLOBALREF_TAG)
376376
if g.mod === Main && isdefined(g.mod, g.name) && isconst(g.mod, g.name)
377-
v = eval(g)
377+
v = getfield(g.mod, g.name)
378378
if isa(v, DataType) && v === v.name.primary && should_send_whole_type(s, v)
379379
# handle references to types in Main by sending the whole type.
380380
# needed to be able to send nested functions (#15451).
@@ -393,17 +393,16 @@ function serialize(s::AbstractSerializer, t::TypeName)
393393
serialize_cycle(s, t) && return
394394
writetag(s.io, TYPENAME_TAG)
395395
write(s.io, object_number(t))
396-
serialize(s, t.name)
397-
serialize(s, t.module)
398-
serialize_typename_body(s, t)
396+
serialize_typename(s, t)
399397
end
400398

401-
function serialize_typename_body(s::AbstractSerializer, t::TypeName)
399+
function serialize_typename(s::AbstractSerializer, t::TypeName)
400+
serialize(s, t.name)
402401
serialize(s, t.names)
403402
serialize(s, t.primary.super)
404403
serialize(s, t.primary.parameters)
405404
serialize(s, t.primary.types)
406-
serialize(s, t.primary.size)
405+
serialize(s, isdefined(t.primary, :instance))
407406
serialize(s, t.primary.abstract)
408407
serialize(s, t.primary.mutable)
409408
serialize(s, t.primary.ninitialized)
@@ -419,27 +418,32 @@ function serialize_typename_body(s::AbstractSerializer, t::TypeName)
419418
else
420419
writetag(s.io, UNDEFREF_TAG)
421420
end
421+
nothing
422422
end
423423

424424
# decide whether to send all data for a type (instead of just its name)
425-
function should_send_whole_type(s, t::ANY)
425+
function should_send_whole_type(s, t::DataType)
426426
tn = t.name
427427
if isdefined(tn, :mt)
428428
# TODO improve somehow
429429
# send whole type for anonymous functions in Main
430-
fname = tn.mt.name
430+
name = tn.mt.name
431431
mod = tn.module
432-
toplevel = isdefined(mod, fname) && isdefined(t, :instance) &&
433-
getfield(mod, fname) === t.instance
434-
ishidden = unsafe_load(unsafe_convert(Ptr{UInt8}, fname))==UInt8('#')
435-
return mod === __deserialized_types__ || (mod === Main && (ishidden || !toplevel))
432+
isanonfunction = mod === Main && # only Main
433+
t.super === Function && # only Functions
434+
unsafe_load(unsafe_convert(Ptr{UInt8}, tn.name)) == UInt8('#') && # hidden type
435+
(!isdefined(mod, name) || t != typeof(getfield(mod, name))) # XXX: 95% accurate test for this being an inner function
436+
# TODO: more accurate test? (tn.name !== "#" name)
437+
#TODO: iskw = startswith(tn.name, "#kw#") && ???
438+
#TODO: iskw && return send-as-kwftype
439+
return mod === __deserialized_types__ || isanonfunction
436440
end
437441
return false
438442
end
439443

440444
# `type_itself` means we are serializing a type object. when it's false, we are
441445
# sending the type tag part of some other object's representation.
442-
function serialize_type_data(s, t::ANY, type_itself::Bool)
446+
function serialize_type_data(s, t::DataType, type_itself::Bool)
443447
whole = should_send_whole_type(s, t)
444448
form = type_itself ? UInt8(0) : UInt8(1)
445449
if whole
@@ -724,68 +728,66 @@ function deserialize(s::AbstractSerializer, ::Type{Union})
724728
Union{types...}
725729
end
726730

727-
module __deserialized_types__
728-
end
729-
731+
module __deserialized_types__ end
730732

731733
function deserialize(s::AbstractSerializer, ::Type{TypeName})
734+
# the deserialize_cycle call can be delayed, since neither
735+
# Symbol nor Module will use the backref table
732736
number = read(s.io, UInt64)
733-
name = deserialize(s)
734-
mod = deserialize(s)
735-
if haskey(known_object_data, number)
736-
tn = known_object_data[number]::TypeName
737-
name = tn.name
738-
mod = tn.module
739-
makenew = false
740-
elseif isdefined(mod, name)
741-
tn = getfield(mod, name).name
742-
# TODO: confirm somehow that the types match
743-
name = tn.name
744-
mod = tn.module
737+
return deserialize_typename(s, number)
738+
end
739+
740+
function deserialize_typename(s::AbstractSerializer, number)
741+
name = deserialize(s)::Symbol
742+
tn = get(known_object_data, number, nothing)
743+
if tn !== nothing
745744
makenew = false
746745
else
747-
name = gensym()
748-
mod = __deserialized_types__
749-
tn = ccall(:jl_new_typename_in, Ref{TypeName}, (Any, Any), name, mod)
746+
# reuse the same name for the type, if possible, for nicer debugging
747+
tn_name = isdefined(__deserialized_types__, name) ? gensym() : name
748+
tn = ccall(:jl_new_typename_in, Ref{TypeName}, (Any, Any),
749+
tn_name, __deserialized_types__)
750750
makenew = true
751+
known_object_data[number] = tn
752+
end
753+
if !haskey(object_numbers, tn)
754+
# setup up reverse mapping for serialize
755+
object_numbers[tn] = number
751756
end
752757
deserialize_cycle(s, tn)
753-
deserialize_typename_body(s, tn, number, name, mod, makenew)
754-
makenew && (known_object_data[number] = tn)
755-
return tn
756-
end
757758

758-
function deserialize_typename_body(s::AbstractSerializer, tn, number, name, mod, makenew)
759-
names = deserialize(s)
760-
super = deserialize(s)
761-
parameters = deserialize(s)
762-
types = deserialize(s)
763-
size = deserialize(s)
764-
abstr = deserialize(s)
765-
mutable = deserialize(s)
766-
ninitialized = deserialize(s)
759+
names = deserialize(s)::SimpleVector
760+
super = deserialize(s)::Type
761+
parameters = deserialize(s)::SimpleVector
762+
types = deserialize(s)::SimpleVector
763+
has_instance = deserialize(s)::Bool
764+
abstr = deserialize(s)::Bool
765+
mutable = deserialize(s)::Bool
766+
ninitialized = deserialize(s)::Int32
767767

768768
if makenew
769769
tn.names = names
770+
# TODO: there's an unhanded cycle in the dependency graph at this point:
771+
# while deserializing super and/or types, we may have encountered
772+
# tn.primary and throw UndefRefException before we get to this point
770773
tn.primary = ccall(:jl_new_datatype, Any, (Any, Any, Any, Any, Any, Cint, Cint, Cint),
771774
tn, super, parameters, names, types,
772775
abstr, mutable, ninitialized)
773776
ty = tn.primary
774-
ccall(:jl_set_const, Void, (Any, Any, Any), mod, name, ty)
775-
if !isdefined(ty,:instance)
776-
if isempty(parameters) && !abstr && size == 0 && (!mutable || isempty(names))
777-
setfield!(ty, :instance, ccall(:jl_new_struct, Any, (Any,Any...), ty))
778-
end
777+
ccall(:jl_set_const, Void, (Any, Any, Any), tn.module, tn.name, ty)
778+
if has_instance && !isdefined(ty, :instance)
779+
# use setfield! directly to avoid `fieldtype` lowering expecting to see a Singleton object already on ty
780+
Core.setfield!(ty, :instance, ccall(:jl_new_struct, Any, (Any, Any...), ty))
779781
end
780782
end
781783

782784
tag = Int32(read(s.io, UInt8)::UInt8)
783785
if tag != UNDEFREF_TAG
784786
mtname = handle_deserialize(s, tag)
785787
defs = deserialize(s)
786-
maxa = deserialize(s)
788+
maxa = deserialize(s)::Int
787789
if makenew
788-
tn.mt = ccall(:jl_new_method_table, Any, (Any, Any), name, mod)
790+
tn.mt = ccall(:jl_new_method_table, Any, (Any, Any), name, tn.module)
789791
tn.mt.name = mtname
790792
tn.mt.defs = defs
791793
tn.mt.max_args = maxa
@@ -798,6 +800,7 @@ function deserialize_typename_body(s::AbstractSerializer, tn, number, name, mod,
798800
end
799801
end
800802
end
803+
return tn::TypeName
801804
end
802805

803806
function deserialize_datatype(s::AbstractSerializer)
@@ -845,7 +848,8 @@ function deserialize(s::AbstractSerializer, t::DataType)
845848
return ccall(:jl_new_struct, Any, (Any,Any...), t)
846849
elseif isbits(t)
847850
if nf == 1
848-
return ccall(:jl_new_struct, Any, (Any,Any...), t, deserialize(s))
851+
f1 = deserialize(s)
852+
return ccall(:jl_new_struct, Any, (Any,Any...), t, f1)
849853
elseif nf == 2
850854
f1 = deserialize(s)
851855
f2 = deserialize(s)

test/parallel_exec.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,34 @@ end
10231023
# issue #15451
10241024
@test remotecall_fetch(x->(y->2y)(x)+1, workers()[1], 3) == 7
10251025

1026+
# issue #16091
1027+
type T16091 end
1028+
wid = workers()[1]
1029+
@test try
1030+
remotecall_fetch(()->T16091, wid)
1031+
false
1032+
catch ex
1033+
((ex::RemoteException).captured::CapturedException).ex === UndefVarError(:T16091)
1034+
end
1035+
@test try
1036+
remotecall_fetch(identity, wid, T16091)
1037+
false
1038+
catch ex
1039+
((ex::RemoteException).captured::CapturedException).ex === UndefVarError(:T16091)
1040+
end
1041+
1042+
f16091a() = 1
1043+
remotecall_fetch(()->eval(:(f16091a() = 2)), wid)
1044+
@test remotecall_fetch(f16091a, wid) === 2
1045+
@test remotecall_fetch((myid)->remotecall_fetch(f16091a, myid), wid, myid()) === 1
1046+
1047+
# these will only heisen-fail, since it depends on the gensym counter collisions:
1048+
f16091b = () -> 1
1049+
remotecall_fetch(()->eval(:(f16091b = () -> 2)), wid)
1050+
@test remotecall_fetch(f16091b, 2) === 1
1051+
@test remotecall_fetch((myid)->remotecall_fetch(f16091b, myid), wid, myid()) === 2
1052+
1053+
10261054
# issue #16451
10271055
rng=RandomDevice()
10281056
retval = @parallel (+) for _ in 1:10

test/serialize.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,21 @@ create_serialization_stream() do s # Base generic function
278278
@test deserialize(s)() === 1
279279
end
280280

281+
# Anonymous Functions
282+
create_serialization_stream() do s
283+
local g() = :magic_token_anon_fun_test
284+
serialize(s, g)
285+
serialize(s, g)
286+
287+
seekstart(s)
288+
local g2 = deserialize(s)
289+
@test g2 !== g
290+
@test g2() == :magic_token_anon_fun_test
291+
@test g2() == :magic_token_anon_fun_test
292+
@test deserialize(s) === g2
293+
end
294+
295+
281296
# Task
282297
create_serialization_stream() do s # user-defined type array
283298
f = () -> begin task_local_storage(:v, 2); return 1+1 end

0 commit comments

Comments
 (0)