Chapter 7

Processes

Every program so far has been one process. Ether programs can be many processes at once, each with its own values, instructions and macros, which share nothing and talk only by sending each other messages.

A process is a table

Chapter 1 described a process as values, instructions and macros. It is literally a table holding those, plus one more stack, the handler stack, which trust uses. process pushes the table of the running process:

process keys sort
values: ( ( "h" "i" "m" "v" ) )

The keys are v (values), i (instructions), m (macros) and h (handler).

Queues

Processes talk through queues. queue makes one and pushes a pair of its two ends: an output, which you send into, and an input, which you receive from. unpair takes the pair apart. send takes a message and an output; receive takes an input and pushes the next message, waiting until one arrives.

queue unpair = out = in
" hello " out send
in receive debug
hello
values: ( )

A message is copied as it is sent. A thawed object that goes into a queue comes out as a separate object, so the sender and the receiver never share anything that can change:

queue unpair = out = in
stack " x " swap push = mine
mine out send
in receive = theirs
" y " mine push del
mine size debug
theirs size debug
2
1
values: ( )

poll asks whether a message is waiting, without waiting.

Fork

fork takes a list of lists. Every list but the last runs in a new copy of this process, and the last one runs here. Each copy then carries on with the rest of the program, so the words after the fork run once in every process:

{ { " in the copy " debug } { " here " debug } } fork
" after the fork " debug
here
after the fork
in the copy
after the fork
values: ( )
values: ( )

The output ends with one values: line per process. A copy starts with everything this process had, including any queue it made earlier, so a queue made before the fork connects the copies:

queue unpair = out = in
{ { # 6 # 7 * out send } { in receive debug } } fork
42
values: ( )
values: ( )

Each process takes its turn to run, and a process waiting in receive lets the others go first. If a process waits for a message that can never come, because nothing that could send to that queue is still running, the wait ends with an exception instead of hanging.

Exercise 7.1

Fork a copy that adds up the numbers from 1 to 10 and sends the total to the original process, which prints it.

A correct answer prints:

55
values: ( )
values: ( )
One answer
queue unpair = out = in
{ { # 0 [ # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9 # 10 ] { + } foreach out send }
  { in receive debug } } fork
55
values: ( )
values: ( )

Exercise 7.2

Fork two copies. One sends 20 and the other sends 22; the original receives both messages and prints their sum.

A correct answer prints:

42
values: ( )
values: ( )
values: ( )
One answer
queue unpair = out = in
{ { # 20 out send } { # 22 out send } { in receive in receive + debug } } fork
42
values: ( )
values: ( )
values: ( )

Exercise 7.3

Send a stack holding a through a queue, then push b onto the original stack. Print the size of the stack that was received, and then the size of the original.

A correct answer prints:

1
2
values: ( )
One answer
queue unpair = out = in
stack \ a swap push = s
s out send
\ b s push del
in receive size debug
s size debug
1
2
values: ( )