require "./parallel" module Fiber::ExecutionContext # Concurrent-only execution context. # # Fibers running in the same context can only run concurrently and never in # parallel to each others. However, they still run in parallel to fibers # running in other execution contexts. # # Fibers in this context can use simpler and faster synchronization primitives # between themselves (for example no atomics or thread safety required), but # data shared with other contexts needs to be protected (see `Sync`), and # communication with fibers in other contexts requires safe primitives, for # example `Channel`. # # A blocking fiber blocks the entire context, and thus all the other fibers in # the context. # # For example: we can start a concurrent context to run consumer fibers, while # the default context produces values. Because the consumer fibers will never # run in parallel and don't yield between reading *result* then writing it, we # are not required to synchronize accesses to the value: # # ``` # require "wait_group" # # consumers = Fiber::ExecutionContext::Concurrent.new("consumers") # channel = Channel(Int32).new(64) # wg = WaitGroup.new(32) # # result = 0 # # 32.times do # consumers.spawn do # while value = channel.receive? # # safe, but only for this example: # result = result + value # end # ensure # wg.done # end # end # # 1024.times { |i| channel.send(i) } # channel.close # # # wait for all workers to be done # wg.wait # # p result # => 523776 # ``` # # In practice, we still recommended to always protect shared accesses to a # variable, for example using `Atomic#add` to increment *result* or a `Sync` # primitive for more complex operations. # # NOTE: The `Concurrent` execution context isn't tied to a system thread, and # may switch to another system thread, for example when a fiber is blocked on # a syscall. class Concurrent < Parallel # :nodoc: def self.default : self new("DEFAULT", capacity: 1, hijack: true) end # Creates a `Concurrent` context. The context will only really start when a # fiber is spawned into it. def self.new(name : String) : self new(name, capacity: 1, hijack: false) end # Always raises an `ArgumentError` exception because a concurrent context # cannot be resized. def resize(maximum : Int32) : Nil raise ArgumentError.new("Can't resize a concurrent context") end end @[Deprecated("Use Fiber::ExecutionContext::Concurrent instead.")] alias SingleThreaded = Concurrent end