Is there a way to have a working Array of Atomic? Especially multidimensional? StaticArray won't do.
Code:
ary = [Atomic(Int16).new(0)]
ary.each do |a|
a.add 1
end
p ary
Output:
Atomic(Int16)(@value=0)]
Ah well, the old it's a struct thus copy on value issue. It works if you reset the value into the array: https://carc.in/#/r/8w7a
ary = [Atomic(Int16).new(0)]
ary.each_with_index do |a, i|
a.add 1
ary[i] = a
end
p ary
But of course that operation is no longer atomic.
@jhass Please write code samples directly into the post, for convenience and reliability. carc.in should be only supplementary.
Atomic is not meant to be used inside containers, or anything else. You can just assign them to vars, instance vars or class vars.
I need a dynamic amount. I guess I'll use a linked list or skip list variant.
Using Pointer to malloc a dynamic amount and Atomic::Ops (which is :nodoc:) can help you in this scenario.
I would like to suggest you to use Slice, but you will stumble on the same problem since Slice#[] will return a copy due to Pointer#[] implementation.
@bcardiff All Ops require a pointer as a first argument. Could this be refactored in to Pointer::Atomic for more general use? Atomic(T) would remain the preferred method.
So, I was hit by this as well.
a = Hash(String, Atomic(Int32)).new
a["Bar"] = Atomic(Int32).new(0)
a["Bar"].add(5)
a["Bar"].get # => 0
I guess it would be nice to get some kind of a warning in the docs of Atomic to not use it inside a container.
Maybe you could all explain what you are trying to do with those dynamic atomics.
Yes, reassigning the hash value with the updated atomic makes it completely loose its point.
```
Implement lock free data structures.
I think the question was geared towards some real world example/code. I'm sure I can implement some random "lock free data structure" without a variable amount of atomics :)
The easiest workaround @bararchy is to use boxed atomics. So the atomic operations will be applied on the same memory locations.
a = Hash(String, Box(Atomic(Int32))).new
a["Bar"] = Box.new(Atomic(Int32).new(0))
a["Bar"].object.add(5)
a["Bar"].object.get # => 5
There might be a need to lock the whole hash depending on the kind of updates you want to do.
@jhass I read variants of this algorithm in 2 different code bases but didn't retain links. If the algorithm has a name I don't know it.
Pseudo code:
class LockFreeArray(T)
# macro needed for Reference/Value/Union types.
@slots = Array(Atomic(Array(T)).new 32
def []=(idx, val)
slot, slot_idx = slot_for(idx)
slot[slot_idx].set val
val
end
private def slot_for(idx)
slot_no = Math.log2(idx)
# retrying CAS loop to get or set @slots[slot_no]
{ slot, slot_idx }
end
end
@jhass I'm also experimenting with lock free queues/stacks/skip lists. All are textbook implementations.