Skip to content

Hash consing and threading leads to crashes #696

@amilsted

Description

@amilsted
using SymbolicUtils

Threads.nthreads()

SymbolicUtils.ENABLE_HASHCONSING[] = false

function testfunc()
    @syms x::Number y::Number f(::Number)::Number
    e1 = f(x) + y
    e2 = f(cos(x)) + e1^2
    e3 = sin(e2)
    substitute(e3, Dict(f(x)=>2))
end

# Threads without hash consing: works!
@Threads.threads for i in 1:100
    testfunc()
end

SymbolicUtils.ENABLE_HASHCONSING[] = true

# Hash consing without threads: works!
empty!(SymbolicUtils.wvd)
for i in 1:100
    testfunc()
end

# Hash consing with threads: exceptions or segfaults! May need to run more than once.
empty!(SymbolicUtils.wvd)
@Threads.threads for i in 1:100
    testfunc()
end

I have a guess at what might be going wrong. It looks like the code in arguments() here initializes the arguments vector without any locking. Hash consing might mean that two threads with apparently independently-defined symbols with the same content now call arguments on the same newly-initialized symbolic struct at the same time, so the arguments list could get clobbered.

An example exception:

ERROR: TaskFailedException

    nested task error: UndefRefError: access to undefined reference
    Stacktrace:
      [1] getindex
        @ ./essentials.jl:13 [inlined]
      [2] iterate
        @ ./array.jl:945 [inlined]
      [3] _foldl_impl
        @ ./reduce.jl:60 [inlined]
      [4] foldl_impl
        @ ./reduce.jl:48 [inlined]
      [5] mapfoldl_impl(f::typeof(SymbolicUtils.symtype), op::SymbolicUtils.var"#37#38"{}, nt::Base._InitialValue, itr::Vector{…})
        @ Base ./reduce.jl:44
      [6] mapfoldl(f::Function, op::Function, itr::Vector{SymbolicUtils.BasicSymbolic{Number}}; init::Base._InitialValue)
        @ Base ./reduce.jl:175
      [7] mapfoldl
        @ ./reduce.jl:175 [inlined]
      [8] _promote_symtype(f::Function, args::Vector{SymbolicUtils.BasicSymbolic{Number}})
        @ SymbolicUtils ~/.julia/packages/SymbolicUtils/2cE5G/src/types.jl:1181
      [9] maketerm(T::Type{…}, head::Function, args::Vector{…}, metadata::Nothing)
        @ SymbolicUtils ~/.julia/packages/SymbolicUtils/2cE5G/src/types.jl:777
     [10] substitute(expr::SymbolicUtils.BasicSymbolic{Number}, dict::Dict{SymbolicUtils.BasicSymbolic{…}, Int64}; fold::Bool)
        @ SymbolicUtils ~/.julia/packages/SymbolicUtils/2cE5G/src/substitute.jl:34
     [11] substitute
        @ ~/.julia/packages/SymbolicUtils/2cE5G/src/substitute.jl:16 [inlined]
     [12] (::SymbolicUtils.var"#343#345"{Bool, Dict{}})(x::SymbolicUtils.BasicSymbolic{Number})
        @ SymbolicUtils ~/.julia/packages/SymbolicUtils/2cE5G/src/substitute.jl:24
     [13] iterate
        @ ./generator.jl:47 [inlined]
     [14] _collect(c::Vector{…}, itr::Base.Generator{…}, ::Base.EltypeUnknown, isz::Base.HasShape{…})
        @ Base ./array.jl:854
     [15] collect_similar
        @ ./array.jl:763 [inlined]
     [16] map
        @ ./abstractarray.jl:3286 [inlined]
     [17] substitute(expr::SymbolicUtils.BasicSymbolic{Number}, dict::Dict{SymbolicUtils.BasicSymbolic{…}, Int64}; fold::Bool)
        @ SymbolicUtils ~/.julia/packages/SymbolicUtils/2cE5G/src/substitute.jl:23
     [18] substitute(expr::SymbolicUtils.BasicSymbolic{Number}, dict::Dict{SymbolicUtils.BasicSymbolic{Number}, Int64})
        @ SymbolicUtils ~/.julia/packages/SymbolicUtils/2cE5G/src/substitute.jl:16
     [19] macro expansion
        @ ./REPL[1]:8 [inlined]
     [20] (::var"#9#threadsfor_fun#12"{var"#9#threadsfor_fun#11#13"{UnitRange{Int64}}})(tid::Int64; onethread::Bool)
        @ Main ./threadingconstructs.jl:215
     [21] #9#threadsfor_fun
        @ ./threadingconstructs.jl:182 [inlined]
     [22] (::Base.Threads.var"#1#2"{var"#9#threadsfor_fun#12"{var"#9#threadsfor_fun#11#13"{UnitRange{Int64}}}, Int64})()
        @ Base.Threads ./threadingconstructs.jl:154

...and 1 more exception.

Stacktrace:
 [1] threading_run(fun::var"#9#threadsfor_fun#12"{var"#9#threadsfor_fun#11#13"{UnitRange{Int64}}}, static::Bool)
   @ Base.Threads ./threadingconstructs.jl:172
 [2] macro expansion
   @ ./threadingconstructs.jl:220 [inlined]
 [3] top-level scope
   @ ./REPL[1]:7
Some type information was truncated. Use `show(err)` to see complete types.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions