Add: julia-0.6.2
Former-commit-id: ccc667cf67d569f3fb3df39aa57c2134755a7551
This commit is contained in:
238
julia-0.6.2/share/julia/base/locks.jl
Normal file
238
julia-0.6.2/share/julia/base/locks.jl
Normal file
@@ -0,0 +1,238 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
import Base: _uv_hook_close, unsafe_convert,
|
||||
lock, trylock, unlock, islocked
|
||||
|
||||
export SpinLock, RecursiveSpinLock, Mutex
|
||||
|
||||
|
||||
##########################################
|
||||
# Atomic Locks
|
||||
##########################################
|
||||
|
||||
"""
|
||||
AbstractLock
|
||||
|
||||
Abstract supertype describing types that
|
||||
implement the thread-safe synchronization primitives:
|
||||
`lock`, `trylock`, `unlock`, and `islocked`
|
||||
"""
|
||||
abstract type AbstractLock end
|
||||
|
||||
# Test-and-test-and-set spin locks are quickest up to about 30ish
|
||||
# contending threads. If you have more contention than that, perhaps
|
||||
# a lock is the wrong way to synchronize.
|
||||
"""
|
||||
TatasLock()
|
||||
|
||||
See SpinLock.
|
||||
"""
|
||||
struct TatasLock <: AbstractLock
|
||||
handle::Atomic{Int}
|
||||
TatasLock() = new(Atomic{Int}(0))
|
||||
end
|
||||
|
||||
"""
|
||||
SpinLock()
|
||||
|
||||
Creates a non-reentrant lock.
|
||||
Recursive use will result in a deadlock.
|
||||
Each `lock` must be matched with an `unlock`.
|
||||
|
||||
Test-and-test-and-set spin locks are quickest up to about 30ish
|
||||
contending threads. If you have more contention than that, perhaps
|
||||
a lock is the wrong way to synchronize.
|
||||
|
||||
See also RecursiveSpinLock for a version that permits recursion.
|
||||
|
||||
See also Mutex for a more efficient version on one core or if the lock may be held for a considerable length of time.
|
||||
"""
|
||||
const SpinLock = TatasLock
|
||||
|
||||
function lock(l::TatasLock)
|
||||
while true
|
||||
if l.handle[] == 0
|
||||
p = atomic_xchg!(l.handle, 1)
|
||||
if p == 0
|
||||
return
|
||||
end
|
||||
end
|
||||
ccall(:jl_cpu_pause, Void, ())
|
||||
# Temporary solution before we have gc transition support in codegen.
|
||||
ccall(:jl_gc_safepoint, Void, ())
|
||||
end
|
||||
end
|
||||
|
||||
function trylock(l::TatasLock)
|
||||
if l.handle[] == 0
|
||||
return atomic_xchg!(l.handle, 1) == 0
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function unlock(l::TatasLock)
|
||||
l.handle[] = 0
|
||||
ccall(:jl_cpu_wake, Void, ())
|
||||
return
|
||||
end
|
||||
|
||||
function islocked(l::TatasLock)
|
||||
return l.handle[] != 0
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
RecursiveTatasLock()
|
||||
|
||||
See RecursiveSpinLock.
|
||||
"""
|
||||
struct RecursiveTatasLock <: AbstractLock
|
||||
ownertid::Atomic{Int16}
|
||||
handle::Atomic{Int}
|
||||
RecursiveTatasLock() = new(Atomic{Int16}(0), Atomic{Int}(0))
|
||||
end
|
||||
|
||||
"""
|
||||
RecursiveSpinLock()
|
||||
|
||||
Creates a reentrant lock.
|
||||
The same thread can acquire the lock as many times as required.
|
||||
Each `lock` must be matched with an `unlock`.
|
||||
|
||||
See also SpinLock for a slightly faster version.
|
||||
|
||||
See also Mutex for a more efficient version on one core or if the lock may be held for a considerable length of time.
|
||||
"""
|
||||
const RecursiveSpinLock = RecursiveTatasLock
|
||||
|
||||
function lock(l::RecursiveTatasLock)
|
||||
if l.ownertid[] == threadid()
|
||||
l.handle[] += 1
|
||||
return
|
||||
end
|
||||
while true
|
||||
if l.handle[] == 0
|
||||
if atomic_cas!(l.handle, 0, 1) == 0
|
||||
l.ownertid[] = threadid()
|
||||
return
|
||||
end
|
||||
end
|
||||
ccall(:jl_cpu_pause, Void, ())
|
||||
# Temporary solution before we have gc transition support in codegen.
|
||||
ccall(:jl_gc_safepoint, Void, ())
|
||||
end
|
||||
end
|
||||
|
||||
function trylock(l::RecursiveTatasLock)
|
||||
if l.ownertid[] == threadid()
|
||||
l.handle[] += 1
|
||||
return true
|
||||
end
|
||||
if l.handle[] == 0
|
||||
if atomic_cas!(l.handle, 0, 1) == 0
|
||||
l.ownertid[] = threadid()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function unlock(l::RecursiveTatasLock)
|
||||
@assert(l.ownertid[] == threadid(), "unlock from wrong thread")
|
||||
@assert(l.handle[] != 0, "unlock count must match lock count")
|
||||
if l.handle[] == 1
|
||||
l.ownertid[] = 0
|
||||
l.handle[] = 0
|
||||
ccall(:jl_cpu_wake, Void, ())
|
||||
else
|
||||
l.handle[] -= 1
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function islocked(l::RecursiveTatasLock)
|
||||
return l.handle[] != 0
|
||||
end
|
||||
|
||||
|
||||
##########################################
|
||||
# System Mutexes
|
||||
##########################################
|
||||
|
||||
# These are mutexes from libuv. We're doing some error checking (and
|
||||
# paying for it in overhead), but regardless, in some situations,
|
||||
# passing a bad parameter will cause an abort.
|
||||
|
||||
# TODO: how defensive to get, and how to turn it off?
|
||||
# TODO: how to catch an abort?
|
||||
|
||||
const UV_MUTEX_SIZE = ccall(:jl_sizeof_uv_mutex, Cint, ())
|
||||
|
||||
"""
|
||||
Mutex()
|
||||
|
||||
These are standard system mutexes for locking critical sections of logic.
|
||||
|
||||
On Windows, this is a critical section object,
|
||||
on pthreads, this is a `pthread_mutex_t`.
|
||||
|
||||
See also SpinLock for a lighter-weight lock.
|
||||
"""
|
||||
mutable struct Mutex <: AbstractLock
|
||||
ownertid::Int16
|
||||
handle::Ptr{Void}
|
||||
function Mutex()
|
||||
m = new(zero(Int16), Libc.malloc(UV_MUTEX_SIZE))
|
||||
ccall(:uv_mutex_init, Void, (Ptr{Void},), m.handle)
|
||||
finalizer(m, _uv_hook_close)
|
||||
return m
|
||||
end
|
||||
end
|
||||
|
||||
unsafe_convert(::Type{Ptr{Void}}, m::Mutex) = m.handle
|
||||
|
||||
function _uv_hook_close(x::Mutex)
|
||||
h = x.handle
|
||||
if h != C_NULL
|
||||
x.handle = C_NULL
|
||||
ccall(:uv_mutex_destroy, Void, (Ptr{Void},), h)
|
||||
Libc.free(h)
|
||||
nothing
|
||||
end
|
||||
end
|
||||
|
||||
function lock(m::Mutex)
|
||||
if m.ownertid == threadid()
|
||||
return
|
||||
end
|
||||
# Temporary solution before we have gc transition support in codegen.
|
||||
# This could mess up gc state when we add codegen support.
|
||||
gc_state = ccall(:jl_gc_safe_enter, Int8, ())
|
||||
ccall(:uv_mutex_lock, Void, (Ptr{Void},), m)
|
||||
ccall(:jl_gc_safe_leave, Void, (Int8,), gc_state)
|
||||
m.ownertid = threadid()
|
||||
return
|
||||
end
|
||||
|
||||
function trylock(m::Mutex)
|
||||
if m.ownertid == threadid()
|
||||
return true
|
||||
end
|
||||
r = ccall(:uv_mutex_trylock, Cint, (Ptr{Void},), m)
|
||||
if r == 0
|
||||
m.ownertid = threadid()
|
||||
end
|
||||
return r == 0
|
||||
end
|
||||
|
||||
function unlock(m::Mutex)
|
||||
@assert(m.ownertid == threadid(), "unlock from wrong thread")
|
||||
m.ownertid = 0
|
||||
ccall(:uv_mutex_unlock, Void, (Ptr{Void},), m)
|
||||
return
|
||||
end
|
||||
|
||||
function islocked(m::Mutex)
|
||||
return m.ownertid != 0
|
||||
end
|
||||
Reference in New Issue
Block a user