Multi-threads error (undefRefError) in simple ABM with Agents.jl

thanks to @Dan great idea of using ThreadSafeDicts.jl and as soon as Enlarge DictABM and VecABM for dispatch by Tortar · Pull Request #993 · JuliaDynamics/Agents.jl · GitHub is merged, ThreadSafeDicts is usable in the dev version of Agents.jl (soon v6). I have ported your MWE to v6 (while improving some other perf aspects of it), you can run it with more than 1 thread and it works:

using Agents, Random, Distributed, ThreadSafeDicts, BenchmarkTools

@agent struct Person(NoSpaceAgent)
    type::Symbol
    height::Int64
end

function random_h!(person, model)
    person.height = rand(abmrng(model), 1:100)
end

function die!(person, model)
    if person.height < model.height_to_die
        remove_agent!(person, model)
    end
end

function add_people!(person, model)
    if person.type === :young && person.height > model.height_to_generate
        for _ in 1:10
            add_agent!(model, rand(abmrng(model), (:baby, :young, :adult)), rand(abmrng(model), 1:100))
        end
    elseif person.type === :adult && person.height > model.height_to_generate
        for _ in 1:10
            add_agent!(model, rand(abmrng(model), (:baby, :young, :adult)), rand(abmrng(model), 1:100))
        end
    end
end

function evolve_environment!(model)
    sum_h = 0.0
    for person in allagents(model)
        sum_h += person.height
    end
    model.sum_h[] = sum_h
end

function complex_step!(model)
    all_ids = collect(allids(model))
    Threads.@threads for i in all_ids
    	person = model[i]
        random_h!(person, model)
        die!(person, model)
    end
    young_adults_ids = collect(Iterators.filter(id -> model[id].type === :young || model[id].type === :adult, allids(model)))
    for i in young_adults_ids
    	person = model[i]
        add_people!(person, model)
    end
    evolve_environment!(model)
    #println("agents now: $(nagents(model))")
end

function create_model()
	properties = (height_to_die = 30, height_to_generate = 90, sum_h = Ref(0.0))
	model = StandardABM(Person; model_step! = complex_step!, properties, 
			    scheduler = Schedulers.fastest, container = ThreadSafeDict,
			    rng = Xoshiro(42))
	for i in 1:100
	    add_agent!(model, rand(abmrng(model), (:baby, :young, :adult)), rand(1:100))
	end
	return model
end

@benchmark run!(create_model(), 10)

you can read a rough version of the docs for the new version at Introduction · Agents.jl

edit:

even if it works, it is actually 2x slower than the single-thread version with 12 threads :smiley: this is somewhat expected, because it’s not easy to do fast multi-threading with non-vector data structures, maybe it is even true that it is faster in the threaded loop, and then it would mean that it is slower because the other parts are not in multi-threading and the multi-threaded structure is slower (maybe it uses a normal Dict with 1 thread?), not sure about that.

unrelated: I just saw now that you use a type field, if you happen to need multiple types you can use the new multiagent macro, see a perf comparison at Performance Tips · Agents.jl