Skip to content

Commit

Permalink
Add support for Atomic{Bool} (Fix JuliaLang#26542).
Browse files Browse the repository at this point in the history
Adds `Bool` to list of types supported by `Atomic{T}`.

Defines all `atomic_*!` for `Bool`, except `atomic_add!` and `atomic_sub!`
since `add(::Bool, ::Bool)` returns an `Int`.

Also adds tests for those methods to `test/threads.jl`.
  • Loading branch information
NHDaly committed Mar 23, 2018
1 parent c2efb04 commit 3df8459
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 10 deletions.
24 changes: 15 additions & 9 deletions base/atomics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ else
end
const floattypes = (Float16, Float32, Float64)
# TODO: Support Bool, Ptr
const atomictypes = (inttypes..., floattypes...)
const arithmetictypes = (inttypes..., floattypes...)
const atomictypes = (arithmetictypes..., Bool)
const IntTypes = Union{inttypes...}
const FloatTypes = Union{floattypes...}
const ArithmeticTypes = Union{arithmetictypes...}
const AtomicTypes = Union{atomictypes...}

"""
Expand All @@ -39,8 +41,8 @@ Holds a reference to an object of type `T`, ensuring that it is only
accessed atomically, i.e. in a thread-safe manner.
Only certain "simple" types can be used atomically, namely the
primitive integer and float-point types. These are `Int8`...`Int128`,
`UInt8`...`UInt128`, and `Float16`...`Float64`.
primitive boolean, integer, and float-point types. These are `Bool`,
`Int8`...`Int128`, `UInt8`...`UInt128`, and `Float16`...`Float64`.
New atomic objects can be created from a non-atomic values; if none is
specified, the atomic object is initialized with zero.
Expand Down Expand Up @@ -130,11 +132,12 @@ julia> x[]
function atomic_xchg! end

"""
Threads.atomic_add!(x::Atomic{T}, val::T) where T
Threads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypes
Atomically add `val` to `x`
Performs `x[] += val` atomically. Returns the **old** value.
Performs `x[] += val` atomically. Returns the **old** value. Not defined for
`Atomic{Bool}`.
For further details, see LLVM's `atomicrmw add` instruction.
Expand All @@ -153,11 +156,12 @@ julia> x[]
function atomic_add! end

"""
Threads.atomic_sub!(x::Atomic{T}, val::T) where T
Threads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypes
Atomically subtract `val` from `x`
Performs `x[] -= val` atomically. Returns the **old** value.
Performs `x[] -= val` atomically. Returns the **old** value. Not defined for
`Atomic{Bool}`.
For further details, see LLVM's `atomicrmw sub` instruction.
Expand Down Expand Up @@ -317,7 +321,7 @@ unsafe_convert(::Type{Ptr{T}}, x::Atomic{T}) where {T} = convert(Ptr{T}, pointer
setindex!(x::Atomic{T}, v) where {T} = setindex!(x, convert(T, v))

const llvmtypes = IdDict{Any,String}(
Bool => "i1",
Bool => "i8", # julia represents bools with 8-bits for now. # TODO: is this okay?
Int8 => "i8", UInt8 => "i8",
Int16 => "i16", UInt16 => "i16",
Int32 => "i32", UInt32 => "i32",
Expand Down Expand Up @@ -380,13 +384,15 @@ for typ in atomictypes
unsafe_convert(Ptr{$typ}, x), cmp, new)
end

for rmwop in [:xchg, :add, :sub, :and, :nand, :or, :xor, :max, :min]
arithmetic_ops = [:add, :sub]
for rmwop in [arithmetic_ops..., :xchg, :and, :nand, :or, :xor, :max, :min]
rmw = string(rmwop)
fn = Symbol("atomic_", rmw, "!")
if (rmw == "max" || rmw == "min") && typ <: Unsigned
# LLVM distinguishes signedness in the operation, not the integer type.
rmw = "u" * rmw
end
if rmwop in arithmetic_ops && !(typ <: ArithmeticTypes) continue end
if typ <: Integer
@eval $fn(x::Atomic{$typ}, v::$typ) =
llvmcall($"""
Expand Down
19 changes: 18 additions & 1 deletion test/threads.jl
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,27 @@ end
end

# Ensure only LLVM-supported types can be atomic
@test_throws TypeError Atomic{Bool}
@test_throws TypeError Atomic{BigInt}
@test_throws TypeError Atomic{ComplexF64}

function test_atomic_bools()
x = Atomic{Bool}(false)
# Arithmetic functions are not defined.
@test_throws MethodError atomic_add!(x, true)
@test_throws MethodError atomic_sub!(x, true)
# All the rest are:
for v in [true, false]
@test x[] == atomic_xchg!(x, v)
@test v == atomic_cas!(x, v, !v)
end
x = Atomic{Bool}(false)
@test false == atomic_max!(x, true); @test x[] == true
x = Atomic{Bool}(true)
@test true == atomic_and!(x, false); @test x[] == false
end

test_atomic_bools()

# Test atomic memory ordering with load/store
mutable struct CommBuf
var1::Atomic{Int}
Expand Down

0 comments on commit 3df8459

Please sign in to comment.