diff --git a/base/condition.jl b/base/condition.jl index 333075b7bfd6d..17d6b12f00df2 100644 --- a/base/condition.jl +++ b/base/condition.jl @@ -22,7 +22,6 @@ function trylock end function islocked end unlockall(l::AbstractLock) = unlock(l) # internal function for implementing `wait` relockall(l::AbstractLock, token::Nothing) = lock(l) # internal function for implementing `wait` -assert_havelock(l::AbstractLock) = assert_havelock(l, Threads.threadid()) assert_havelock(l::AbstractLock, tid::Integer) = (islocked(l) && tid == Threads.threadid()) ? nothing : concurrency_violation() assert_havelock(l::AbstractLock, tid::Task) = diff --git a/base/lock.jl b/base/lock.jl index 60066220b744e..ad728280c43e9 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -16,6 +16,7 @@ mutable struct ReentrantLock <: AbstractLock ReentrantLock() = new(nothing, GenericCondition{Threads.SpinLock}(), 0) end +assert_havelock(l::ReentrantLock) = assert_havelock(l, l.locked_by) """ islocked(lock) -> Status (Boolean) diff --git a/base/locks-mt.jl b/base/locks-mt.jl index 2ef1f8e2b2a67..a828adef1f913 100644 --- a/base/locks-mt.jl +++ b/base/locks-mt.jl @@ -30,6 +30,10 @@ struct SpinLock <: AbstractLock SpinLock() = new(Atomic{Int}(0)) end +# Note: this cannot assert that the lock is held by the correct thread, because we do not +# track which thread locked it. Users beware. +Base.assert_havelock(l::SpinLock) = islocked(l) ? nothing : concurrency_violation() + function lock(l::SpinLock) while true if l.handle[] == 0 diff --git a/test/threads_exec.jl b/test/threads_exec.jl index eb5ee45eb0972..2d6797fcf2600 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -151,6 +151,21 @@ end threaded_gc_locked(SpinLock) threaded_gc_locked(Threads.ReentrantLock) +# Issue 33159 +# Make sure that a Threads.Condition can't be used without being locked, on any thread. +@testset "Threads.Conditions must be locked" begin + c = Threads.Condition() + @test_throws Exception notify(c) + @test_throws Exception wait(c) + + # If it's locked, but on the wrong thread, it should still throw an exception + lock(c) + @test_throws Exception fetch(@async notify(c)) + @test_throws Exception fetch(@async notify(c, all=false)) + @test_throws Exception fetch(@async wait(c)) + unlock(c) +end + # Issue 14726 # Make sure that eval'ing in a different module doesn't mess up other threads orig_curmodule14726 = @__MODULE__