Multithreading
Factor handbook » Guided tour of Factor

Prev:Deploying programs
Next:Servers and Furnace


As we have said, the Factor runtime is single-threaded, like Node. Still, one can emulate concurrency in a single-threaded setting by making use of coroutines. These are essentially cooperative threads, which periodically release control with the yield word, so that the scheduler can decide which coroutine to run next.

Although cooperative threads do not allow to make use of multiple cores, they still have some benefits:
input/output operations can avoid blocking the entire runtime, so that one can implement quite performant applications if I/O is the bottleneck;
user interfaces are naturally a multithreaded construct, and they can be implemented in this model, as the listener itself shows;
finally, some problems may just naturally be easier to write making use of the multithreaded constructs.

For the cases where one wants to make use of multiple cores, Factor offers the possibility of spawning other processes and communicating between them with the use of channels, as we will see in a later section.

Threads in Factor are created using a quotation and a name, with the spawn word. Let us use this to print the first few lines of Star Wars, one per second, each line being printed inside its own thread. First, we will assign them to a dynamic variable:
SYMBOL: star-wars "A long time ago, in a galaxy far, far away.... It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire. During the battle, rebel spies managed to steal secret plans to the Empire's ultimate weapon, the DEATH STAR, an armored space station with enough power to destroy an entire planet. Pursued by the Empire's sinister agents, Princess Leia races home aboard her starship, custodian of the stolen plans that can save her people and restore freedom to the galaxy...." " " split star-wars set

We will spawn 18 threads, each one printing a line. The operation that a thread must run amounts to
star-wars get ?nth print

Note that dynamic variables are shared between threads, so each one has access to star-wars. This is fine, since it is read-only, but the usual caveats about shared memory in a multithreaded settings apply.

Let us define a word for the thread workload
: print-a-line ( i -- ) star-wars get ?nth print ;

If we give the i-th thread the name i, our example amounts to
18 [0..b) [ [ [ print-a-line ] curry ] [ number>string ] bi spawn ] each

Note the use of curry to send i to the quotation that prints the i-th line. This is almost what we want, but it runs too fast. We need to put the thread to sleep for a while. So we clear the stack that now contains a lot of thread objects and look for the sleep word in the help.

It turns out that sleep does exactly what we need, but it takes a duration object as input. We can create a duration of i seconds with... well i seconds. So we define
: wait-and-print ( i -- ) dup seconds sleep print-a-line ;

Let us try
18 [0..b) [ [ [ wait-and-print ] curry ] [ number>string ] bi spawn ] each

Instead of spawn, we can also use in-thread which uses a dummy thread name and discards the returned thread, simplifying the above to
18 [0..b) [ [ wait-and-print ] curry in-thread ] each

In serious applications threads will be long-running. In order to make them cooperate, one can use the yield word to signal that the thread has done a unit of work, and other threads can gain control. You also may want to have a look at other words to stop, suspend or resume threads.