General purpose concurrency abstractions Thread-safe value objects, structures and collections Thread-safe variables Threadpools Thread Sychronization classes and algorithms Edge features of concurrent-ruby library
than just Threads & Mutexes You know hazards of using low level features like Threads, Mutexes & Shared memory You are looking for better options to write better concurrent code
utilities without debating which is better or why Remain free of external gem dependencies Stay true to the spirit of the languages providing inspiration But implement in a way that makes sense for Ruby Keep the semantics as idiomatic Ruby as possible Support features that make sense in Ruby Exclude features that don't make sense in Ruby Be small, lean, and loosely coupled
any Ruby concurrency library. The only library with a published memory model which provides consistent behavior and guarantees on all three of the main Ruby interpreters (MRI/CRuby, JRuby, and Rubinius).
No concurrency library for Ruby can ever prevent the user from making thread safety mistakes. All the library can do is provide safe abstractions which encourage safe practices.
than any other Ruby library Many of these abstractions support the mantra of "Do not communicate by sharing memory; instead, share memory by communicating".
end horn = Echo.new horn.echo('zero') # synchronous, not thread-safe # returns the actual return value of the method horn.async.echo('one') # asynchronous, non-blocking, thread-safe # returns an IVar in the :pending state horn.await.echo('two') # synchronous, blocking, thread-safe # returns an IVar in the :complete state General Purpose Concurrency Abstraction
execute as soon as possible. ScheduledTask is set to execute after a specified delay. based on Java's ScheduledExecutorService. General Purpose Concurrency Abstraction
regular intervals Can be done with threads, but exception can cause thread to abnormally end. When a TimerTask is launched it starts a thread for monitoring the execution interval. The TimerTask thread does not perform the task, however. Instead, the TimerTask launches the task on a separate thread. General Purpose Concurrency Abstraction
Maybe either contains a value of (represented as Just), or it is empty (represented as Nothing). Using Maybe is a good way to deal with errors or exceptional cases without resorting to drastic measures such as exceptions. Maybe is a replacement for the use of nil with better type checking. Based on Haskell Data.Maybe. Thread-safe Value Object
ActiveRecord model end # -- if the record was found result.just? #=> true result.value #=> #<Client id: 10, first_name: "Ryan"> # -- if the record was not found result.just? #=> false result.reason #=> ActiveRecord::RecordNotFound Thread-safe Value Object
member can have its value set at most once, either at construction or any time thereafter. Attempting to assign a value to a member that has already been set will result in a Concurrent::ImmutabilityError. Thread-safe Structure
is a shared, mutable variable providing independent, uncoordinated, asynchronous change of individual values. Best used when the value will undergo frequent, complex updates. Suitable when the result of an update does not need to be known immediately Thread-safe variable
methods. These methods always return immediately. The actions of all Agents get interleaved amongst threads in a thread pool. The #send method should be used for actions that are CPU limited. #send_off method is appropriate for actions that may block on IO. Thread-safe variable
set + [set[-2..-1].reduce{|sum,x| sum + x }] end # create an agent with an initial value agent = Concurrent::Agent.new(next_fibonacci) # send a few update requests 5.times do agent.send{|set| next_fibonacci(set) } end # wait for them to complete agent.await # get the current value agent.value #=> [0, 1, 1, 2, 3, 5, 8] Thread-safe variable
state. At any time the value of the atom can be synchronously and safely changed There are two ways to change the value of an atom: #compare_and_set and #swap. Suitable when the result of an update must be known immediately. Thread-safe variable
set + [set[-2..-1].reduce{|sum,x| sum + x }] end # create an atom with an initial value atom = Concurrent::Atom.new(next_fibonacci) # send a few update requests 5.times do atom.swap{|set| next_fibonacci(set) } end # get the current value atom.value #=> [0, 1, 1, 2, 3, 5, 8] Thread-safe variable
are empty or contain one item. Taking a value from an empty MVar blocks, as does putting a value into a full one. Like blocking queue of length one, or a special kind of mutable variable. MVar is a Dereferenceable. Thread-safe variable
different for each thread. Each variable may have a default value, but when you modify the variable only the current thread will ever see that change. Thread-safe variable
a single item container that always contains exactly one value These are shared, mutable variables which provide coordinated, synchronous, change of many at once. Used when multiple value must change together, in an all-or-nothing. Thread-safe variable
on multiple other threads. The thread that will wait creates a CountDownLatch and sets the initial value (normally equal to the number of other threads). The initiating thread passes the latch to the other threads then waits for the other threads by calling the #wait method. When the latch counter reaches zero the waiting thread is unblocked and continues with its work. A CountDownLatch can be used only once. Its value cannot be reset. Thread Synchronization Classes & Algorithms
it is in the unset state. Threads can choose to #wait on the event, blocking until released by another thread. When one thread wants to alert all blocking threads it calls the #set method which will then wake up all listeners. Once an Event has been set it remains set. New threads calling #wait will return immediately. Thread Synchronization Classes & Algorithms
assign. As a future is a value that is being computed that you can wait on… …An IVar is a value that is waiting to be assigned, that you can wait on. IVars are single assignment and deterministic. The IVar becomes the primitive on which futures and dataflow are built. Thread Synchronization Classes & Algorithms
concurrent writer. While the "write" lock is taken, no read locks can be obtained either. Hence, the write lock can also be called an "exclusive" lock. If another thread has taken a read lock, any thread which wants a write lock will block until all the readers release their locks. A thread can acquire both a read and write lock at the same time. A thread can also acquire a read lock OR a write lock more than once. Thread Synchronization Classes & Algorithms
the current thread is holding only a read lock, not a write # lock. So other threads can take read locks, but not a write lock. lock.release_read_lock # Now the current thread is not holding either a read or write lock, so # another thread could potentially acquire a write lock. Concurrent::ReentrantReadWriteLock Thread Synchronization Classes & Algorithms
permits. Each #acquire blocks if necessary until a permit is available, and then takes it. Each #release adds a permit, potentially releasing a blocking acquirer. No permit objects are used, the Semaphore just keeps a count of the number available and acts accordingly. Thread Synchronization Classes & Algorithms
where concurrent actors exchange messages. Channel: Communicating Sequential Processes (CSP). Functionally equivalent to Go channels with additional inspiration from Clojure core.async. LazyRegister AtomicMarkableReference LockFreeLinkedSet LockFreeStack
all the methods lock-free. (with the exception of obviously blocking operations like #wait, #value, etc.). As a result it lowers danger of deadlocking and offers better performance. Edge features
It provides similar tools as other promise libraries do. Users coming from other languages and other promise libraries will find the same tools here Not just another promises implementation. Adds new ideas, and is integrated with other abstractions like actors and channels. Edge features
suitable abstraction, e.g. just promises or actors. If the problem is complex user can combine parts (promises, channels, actors) which were designed to work together well to a solution. Rather than having to combine fragilely independent tools. Edge features
the asynchronous tasks together Create delayed tasks Create scheduled tasks Deal with errors through rejections Reduce danger of deadlocking Control the concurrency level of tasks Simulate thread-like processing without occupying threads Use actors to maintain isolated states and to seamlessly combine it with promises Build parallel processing stream system with back pressure Edge features
<#Concurrent::Promises::Future:0x7fe92c706850 pending> scheduled.resolved? # => false # Value will become available after 0.1 seconds. scheduled.value # => 1 Edge features
Return value is a reference to the actor, the actual actor # is never returned. counter = Counter.spawn(:first, 5) # Tell a message and forget returning self. counter.tell(1) counter << 1 # (First counter now contains 7.) # Send a messages asking for a result. counter.ask(0).value Edge features
Go channels with additional inspiration from Clojure core.async. Every code example in the channel chapters of both “A Tour of Go” and “Go By Example” has been reproduced in Ruby. The code can be found in the examples directory of the concurrent-ruby source repository. Edge features