Issue #8259 has been updated by Charles Nutter.


Responding to recent comments...

* Atomic operations for Array, Hash

I lean toward introducing purpose-built collections for this. The current Array and Hash are very fast and about as lightweight as they can be for their features. Making them support atomic operations and thread-safe access would either make them considerably larger or considerably slower. Larger, because for example Array's internal array of elements and size *must* be updatable atomically, and the only way to do that is to have a structure wrap them. That means at least one more indirection to get to array elements, and a new object allocation every time you want to grow the internal array. Doable, but obviously not as lightweight *or* as fast as what we have now. A similar statement goes for Hash...if you need to update the linked buckets (because remember, we have to maintain insertion order too!) or the bucket array (same problem as in Array), you need to be able to do it atomically. And in both cases, you can only avoid the extra indirection by fully locking all reads a
 nd writes, which then quickly becomes a bottleneck.

If we want atomic/threadsafe collections, they really need to have separate implementations.

* Why introduce atomic ivars/attrs rather than just using Atomic

Atomic works just fine *functionally*, but for every reference you want to update atomically you need to have an Atomic Object around it. on JRuby, on a 64-bit JVM, this is dozens of bytes of additional allocation. It's also an extra indirection we don't want to have for fast atomic access.

* On reference equality only, rather than value equality

I agree as well...we need to expect that all objects are unique pointers, and that they will be compared by identity. Of course, on MRI two different Float or Fixnum "objects" will be identical if they have the same value, while only Fixnums are identical in Rubinius, and only a small range of Fixnums are identical in JRuby. We could, however, add a form of atomic ivar CAS that also considers value, and people can opt into it when they're dealing with e.g. numbers, strings expected to be the same, etc. Honestly, though, value equality is orthogonal to atomic updates...it would be better for users to use the basic reference CAS and then put their own logic around it to check value. See the CAS implementation in the Atomic gem.

* Whether core collections are threadsafe today

They are not, in any implementation. However, MRI prevents *all* concurrency at the Ruby level, so you never notice that the collections aren't thread-safe. JRuby and Rubinius make no guarantees about Array, Hash, String thread-safety. They may not segfault (JVM at least has guards to prevent dereferencing invalid pointers), but they won't work as expected under concurrent modification.

* Atomic versus synchronized versus volatile versus...

I'm sure I'll end up using JVM Memory Model terminology...since no other Ruby implementation has a formal memory model. The definitions on the JVM, roughly:

volatile: Mostly involves ordering of reads and writes. Writes to a variable marked as volatile are guaranteed to "happen" -- propagate throughout the CPU/caches -- before any reads that happen afterwards. It's a little abstract, but basically it's guaranteeing that when you make an update of a volatile variable, any threads that look at that variable will see the value you just wrote, assuming nobody else is writing.

atomic: Performing a read + write operation, perhaps with a comparison, as a single operation that either succeeds or fails as a whole. This is your compare-and-swap, get-and-set (swap), increment-and-get, and so on.

synchronized: Synchronization guarantees that a given body of code will only be executed by a single thread + object combination. So if you have an array in hand and you synchronize access to it, that array's mutators won't run on more than one thread at once. This is not the same as either volatile or atomic, though it can be implemented atop them. Equivalent concept in MRI would be using a mutex or monitor (JVM basically provides a built-in reentrant monitor + condvar on every object).

----------------------------------------
Feature #8259: Atomic attributes accessors
https://bugs.ruby-lang.org/issues/8259#change-47225

* Author: Yura Sokolov
* Status: Open
* Priority: Normal
* Assignee: 
* Category: 
* Target version: Ruby 2.1.0
----------------------------------------
=begin
Motivated by this gist ((<URL:https://gist.github.com/jstorimer/5298581>)) and atomic gem

I propose Class.attr_atomic which will add methods for atomic swap and CAS:

  class MyNode
    attr_accessor :item
    attr_atomic :successor

    def initialize(item, successor)
      @item = item
      @successor = successor
    end
  end
  node = MyNode.new(i, other_node)

  # attr_atomic ensures at least #{attr} reader method exists. May be, it should
  # be sure it does volatile access.
  node.successor

  # #{attr}_cas(old_value, new_value) do CAS: atomic compare and swap
  if node.successor_cas(other_node, new_node)
    print "there were no interleaving with other threads"
  end

  # #{attr}_swap atomically swaps value and returns old value.
  # It ensures that no other thread interleaves getting old value and setting
  # new one by cas (or other primitive if exists, like in Java 8)
  node.successor_swap(new_node)

It will be very simple for MRI cause of GIL, and it will use atomic primitives for
other implementations.

Note: both (({#{attr}_swap})) and (({#{attr}_cas})) should raise an error if instance variable were not explicitly set before.

Example for nonblocking queue: ((<URL:https://gist.github.com/funny-falcon/5370416>))

Something similar should be proposed for Structs. May be override same method as (({Struct.attr_atomic}))

Open question for reader:
should (({attr_atomic :my_attr})) ensure that #my_attr reader method exists?
Should it guarantee that (({#my_attr})) provides 'volatile' access?
May be, (({attr_reader :my_attr})) already ought to provide 'volatile' semantic?
May be, semantic of (({@my_attr})) should have volatile semantic (i doubt for that)?
=end




-- 
https://bugs.ruby-lang.org/