-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Do not cache the type before we finish constructing it. #11606
Conversation
I don't like changes that make weird "working" code raise an error but I think that's the right thing to do.... julia> NTuple{20000, Int}
ERROR: OverflowError()
julia> NTuple{20000, Int}
ERROR: OverflowError()
julia> NTuple{20000, Int}
ERROR: OverflowError()
julia> abstract C{T<:Union(Void, Int)}
julia> type D{T} <: C{T} d::T end
julia> D(1.1)
ERROR: TypeError: C: in T, expected T<:Union(Int64,Void), got Type{Float64}
julia> D{Float64}
ERROR: TypeError: C: in T, expected T<:Union(Int64,Void), got Type{Float64}
julia> D{Float64}
ERROR: TypeError: C: in T, expected T<:Union(Int64,Void), got Type{Float64} |
@@ -2047,6 +2045,7 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i | |||
ndt->ninitialized = ntp; | |||
else | |||
ndt->ninitialized = dt->ninitialized; | |||
if (cacheable) jl_cache_type_(ndt); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
superficial, but I think an extra newline before this would help readability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tkelman I go further and add one before and after as well as making it a two line statement (last one is because I hate one like control flow statement in C/C++).
Would be nice if you can abort the old CI runs...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
432c6f4
to
904ff26
Compare
I think this was on purpose because if you have a recursive type you'd want only one instance created ? |
@carnaval Good point. So is there a remove from cache or temporaryly add to cache function? |
I'm not going through the source atm, could you explain what was the underlying problem ? Where is the cache used before the initialization is over ? |
@carnaval The issue is that the functions after the type is cached can throw an error (for the two cases above, instantiation of super and calculation of the offset) If it happens, the type will stay in the cache with an invalid state. Causing code using them to behave unexpectedly |
And I guess removing from cache may not be enough because it can be referenced to by other types as well.... |
Also please feel free to abort CI... |
The only solution I can think of ATM is to have a temporary global type cache when instantiating the types and only update the real one if everything succeeds. |
Even if it makes things more complicated it may be a case where we should do all the checks upfront and only instantiate when it is guaranteed not to fail. Although this doesn't help for some things like OOM exceptions along the way. Removing from the cache is probably the only fully safe option. We could store a flag on each type saying if it is fully initialized. On error you go through the cache and remove the broken folks. |
I've thought about that too but if I understand correctly the cache is bound to each typenames. Is there a way to iterate all typenames? |
I'm surprised that the test passes.... Maybe we should add some tests for sth like self-refering types? |
Interestingly, the following code doesn't cause an error on my branch. julia> type B{T}
b::B{T}
B() = (b = new(); b.b = b; b)
end
julia> B{Int}()
B{Int64}(B{Int64}(#= circular reference =#))
julia> abstract A{T}
julia> type C{T} <: A{C{T}}
end
julia> C{Int}
C{Int64} @carnaval Any idea why?.... |
@carnaval Actually I think this is probably not a problem. There's a The travis-CI is just the normal OOM error, the 32-bit AppVeyor is something different. I can't tell if it is related. @JeffBezanson I guess you should have the ultimate (if not the only) word here. (Only when you are not busy with other more important stuff though.) |
904ff26
to
b04ea0d
Compare
👍 but I'd like to briefly look into the CI failure. |
@JeffBezanson The failure changes after a rebase. Any hint where to look? e.g. how would the cache interact with other part and why was the cache in that place to begin with (it seems strange to cache sth before it is done constructing....) |
b04ea0d
to
11f5324
Compare
Same failure as in #10380 (comment), maybe tuple related? |
The unpredictability of the failure make me think it might be GC related. I'm still going through the code but I've find one place that looks strange. Is this a missing root or am I seeing things here? Even if tuple types are always cached I feel like this is too weak an assumption to make here. |
@tkelman = = is there a strip down list of those failures................... |
From the blame afd4eb0#diff-757551c8929efe195c9bab04f9925d05R624 it could certainly be #10380 related... |
@yuyichao Yes, the assumption there was that all concrete tuple types will be cached. In fact they have to be, or there will be many other problems. |
In that case, IMHO this commit should only make a difference for functions that check the type cache which is only P.S. what does these |
ref #11320, which appears to also show the same issue (if you try to run the example twice) |
Do not cache the type before we finish constructing it.
@JeffBezanson One of the failure seen in this PR shows up on AppVeyor for a few PR's again..... |
So if this is called recursively, it can miss the stack in |
i don't think checking the stack only is sufficient, since it may still be possible to cache a type that points to a partially constructed type (in this case, julia> type Z{I}
a::Vector{Z{I}}
b::NTuple{20000,I}
end
julia> Z{Int}
ERROR: OverflowError()
julia> Vector{Z{Int}}
Array{Z{Int64},1} |
So should I replace the stack with a temporary cache, make sure it is passed to all functions and only merge them to the global cache after everything is done? |
Or is there a better way to do this? |
I guess someone could still write a very long structure? (or a structure of enough long tuples)? |
I think we'll have to allow more offset bits; uint16 is just too small. |
(And size.) Sure, that works too.... Just wondering if it is this short for some performance argument... |
I just wanted to keep it as compact as possible. Before the new tuples it made sense. |
here's an updated example showing where it is in the cache: julia> abstract A{I}
julia> type Z{I}
a::A{Z{I}}
b::NTuple{20000,I}
end
julia> Z{Int}
ERROR: OverflowError()
julia> A.name.cache[1].parameters[1]
Z{Int64} |
Although even if you increase the size to 32bit, it wouls still be possible to construct invalid types with a few recursions of sth like this without running out of memory julia> type A{T}
a::T
b::T
end |
Actually, it might be preferable to allow making types that are "invalid", and defer errors to run time e.g. when you try to construct an instance. For example you want to be able to ask "is (1,2) a 20000-element tuple?" The question makes sense even if you can't actually construct a 20000-element tuple. We should probably use |
So e.g. it would be possible to construct an array of In this case, should querying the properties of the type return an error? If you want Edit: .... yes -> no |
What if you get an OOM error while doing the instantiating of a type of reasonable size ? Agreed you're pretty much done at this point but if we're not going to even try handling those we might as well replace it with exit(1). |
at some point, |
Yes, exactly. |
After fixing the offsets issue, the |
Please don't take control out of the application developer's hands. Also, printing a big message (in fact, any message) is also not a good idea if you are not at the REPL, or there is no handler for the exception you should give. |
reinstates and hopefully fixes the problem in #11606
reinstates and hopefully fixes the problem in JuliaLang#11606
This fixes the issue mentioned in #11597 (comment) as well as the inconsistency in #11449
@JeffBezanson
@andrewcooke