First stab at implementing RFC 0002 for Crystal.
The ExecutionContext::SingleThreaded
context starts a scheduler on a single
thread. Every fiber spawned into that context will never run in parallel to each
other (but they will run in parallel to fibers running in other contexts).
The ExecutionContext::MultiThreaded
context starts multiple threads and a
scheduler on each of them. Fibers spawned into that context will run in
parallel to each other (and other fibers in other contexts). Pending runnable
fibers may be stolen and resumed by any scheduler inside the context (but never
be stolen and resumed by another context)
The ExecutionContext::Isolated
context starts a single thread to run one
exclusive fiber. Trying to spawn inside this context will actually enqueue the
fibers into another context (the default context by default).
You can spawn a fiber into an explicit execution context using context.spawn
while spawn
will spawn into the current context.
The shard creates a default execution context that is
ExecutionContext::SingleThreaded
by default unless you specify the -Dmt
compilation flag in addition to the -Dpreview_mt
flag (required to make stdlib
object thread safe) in which case it will be ExecutionContext::MultiThreaded
.
The shard replaces the regular Crystal::Scheduler implementation in stdlib with new ones.
The shard will monkey-patch many things into Crystal's stdlib as much as possible, yet there are a couple changes that requires to patch the stdlib directly.
The patches are in the /patches
folder of the shard and must be applied on top
of Crystal HEAD. I recommend to clone the Git repository on your local, and to
keep it up to date:
$ git clone https://github.com/crystal-lang/crystal.git
$ cd crystal
$ for f in /path/to/execution_context/patches/*; do patch -p1 $f; done
Said patches have been merged and shall be released with Crystal 1.13.0. Maybe further changes will need patches again, though.
Add the shard to your project:
dependencies:
execution_context:
github: ysbaddaden/execution_context
branch: main
The we can start creating execution contexts:
require "execution_context"
st = ExecutionContext::SingleThreaded.new("ST")
mt = ExecutionContext::MultiThreaded.new("MT", size: 4)
100.times do |i|
st.spawn do
print "Hello from ST (iteration #{i})\n"
end
mt.spawn do
print "Hello from MT (iteration #{i})\n"
end
end
sleep 1
Remember that you need a patched Crystal and we still need the preview_mt
flag
to enable thread safety in Crystal stdlib (e.g. GC, Channel, Mutex).
$ CRYSTAL=/path/to/patched-crystal/bin/crystal
$ $CRYSTAL run -Dpreview_mt test.cr
Hello from MT (iteration 0)
Hello from MT (iteration 1)
Hello from ST (iteration 2)
Hello from MT (iteration 2)
Hello from ST (iteration 3)
Hello from ST (iteration 0)
Hello from ST (iteration 1)
Hello from MT (iteration 3)
Hello from ST (iteration 4)
Hello from MT (iteration 4)
Hello from ST (iteration 5)
Hello from MT (iteration 5)
...
The messages should be in any order, mixing ST and MT.
Distributed under the Apache-2.0 license.