Skip to content

Differentiating through Threads.@threads with boxed variables leads to race conditions #3005

@penelopeysm

Description

@penelopeysm

The following bug requires a Julia session launched with multiple threads. The primal is deterministic, but the gradient with reverse-mode Enzyme does not always give the correct result:

using Enzyme

struct Container
    vals::Vector{Float64}
end

function f(x)
    a = x[1]
    c = Container(zeros(Threads.maxthreadid()))
    Threads.@threads for i in 1:100
        tid = Threads.threadid()
        c.vals[tid] += a * i
        c = c # forces `c` to be boxed
    end
    return sum(c.vals)
end

julia> unique([f([2.0]) for _ in 1:20])
1-element Vector{Float64}:
 10100.0

julia> unique([Enzyme.gradient(Enzyme.Reverse, f, [2.0]) for _ in 1:20])
4-element Vector{Tuple{Vector{Float64}}}:
 ([5050.0],)
 ([4950.0],)
 ([4995.0],)
 ([4658.0],)

julia> versioninfo()
Julia Version 1.11.9
Commit 53a02c0720c (2026-02-06 00:27 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: macOS (arm64-apple-darwin24.0.0)
  CPU: 10 × Apple M1 Pro
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, apple-m1)
Threads: 8 default, 0 interactive, 4 GC (on 8 virtual cores)

(ppl) pkg> st
Status `~/ppl/Project.toml`
  [7da242da] Enzyme v0.13.132

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