Initially we inject one instance of remains n and n instances of the job Each job Let us test this: each job will simply print a digit from 0 to 9. This solution is flawed in several ways. At the end of the calculation shown above, the soup still contains four job However, there are no remains n molecules, so no further reactions are actually running.
Nothing prevents us from injecting several remains n molecules into the soup at the same time, with different values of "n". We could prohibit this by encapsulating the remains n molecules, so that the user cannot make a mistake when injecting the molecules. There is another, more serious flaw in the present code; can you see it?
The flaw is that the remains n molecule is consumed by the job reaction and injected only when a job is finished.
So only one job can run at any one time. We lost the concurrency! In order to restore the concurrency, we need to make sure that remains n molecule is always present in the soup. The updating of the value of "n" must be synchronous, but we would like this to be done while other jobs are running. Therefore, we need another reaction for updating of the value of "n" in remains n.
This reaction must be triggered asynchronously at the end of each job; let us define a triggering molecule for this, called done. The updating reaction could look like this:. The process looks like this: we inject several "job" molecules and one "remains" molecule. All jobs molecules can start at once, producing eventually a bunch of "done" molecules.
These "done" molecules react one by one with the single "remains" molecule. All the "job" reactions can run in parallel, but only one "remains" reaction runs at any one time. An alternative implementation is to make the "done" molecule an instant molecule. Then the reactions look like this:. Now each "job" reaction starts concurrently but blocks at the instant "done" call. It seems that the chemical model is quite flexible and allows us to configure the concurrent computations in pretty much any manner.
Now let us figure out the implementation of these examples in join calculus. We will be using the purely "chemical" approach to concurrent computation.
We will never say the words "semaphore", "thread", "deadlock", "mutex", or "synchronize". Instead, we will talk about molecules and reactions. Our goal is to see what kind of tricks and patterns emerge from this paradigm. In order to implement a concurrent computation of many functions, one might want to define a separate molecule for each array element. However, this is not possible in the join calculus. The join calculus has these limitations, beyond those I described before:. Nevertheless, it seems that the join calculus can express essentially all concurrent computations.
We will implement the examples now. Each evaluation of a function must be a separate reaction. The payload of that reaction can assign the element of the array with the result value. So we only need one molecule for this reaction. Let this molecule be called "a" and carry, as decoration values, the function, the array, and the array index. This works; however, the "results" array is updated asynchronously, and the final array will be ready at an unknown time when all reactions are finished.
Certainly, we would like to be notified when this happens! As we have seen before, we need a counter that will show how many computations remain. This counter will start a new reaction when all computations are finished. This counter also needs a separate reaction for updating itself. How will we write the reaction for the counter? It is impossible in join calculus to wait until no more "a" molecules are present. Therefore, the counter should explicitly know how many reactions were started.
To maintain the counter, we need to check how many "a" reactions have been completed. Let us produce an additional molecule, "ready", at the end of each "a" reaction, so that the counter could react with the "ready" molecules. The advantage of this approach is that the counting is performed asynchronously, independently of the "a" reaction. Thus, all "a" reactions can run in parallel, producing many "ready" molecules, while the "remains" molecule counts and consumes each of the "ready" molecules one by one.
This code works; here is the full test code for JoCaml.
I added some printing so that we can watch the progress. As we can see, the tasks were processed concurrently in somewhat arbitrary order. We need to add some pairs, then add partial sums, etc. A first idea would be to inject molecules a 10 , a 20 , a 30 , a 40 , a 50 and organize reactions such that all numbers are eventually added together. It remains to define the necessary reactions.
How can we put two "a" molecules together in a reaction? Obviously, we need new input molecules here. We will not have succeeded in bringing a pair of "a" molecules together. So we need two further molecules and two reactions, say, like this:.
Note that we use the "or" keyword; this is required in JoCaml for reactions that use the same input molecules. Then the number of "b" molecules will always remain equal to the number of "a" molecules. So we find that the restriction to non-repeating molecules is not really a limitation of the expressive power of the join calculus. We can always define additional molecules and reactions to achieve the same effect as in a reaction with repeating molecules. Having defined these reactions, we will have achieved that eventually all "a x " molecules will come together and produce a single "a z " molecule, whose value z will be equal to the sum of all previously given values.
However, we again face the same problem as in the previous example: namely, we will not know when this happens. There is no way to check that only one molecule "a x " is present in the soup.
It seems that we again need some kind of "counter" that keeps track of how much remains to be done. What kind of reaction will this counter have? If we use the same "counter" technique as in the previous example, we will force the summation to proceed serially: the "counter" consumes each "ready" molecule one by one.
So here we need a different tecnique. It seems that we need to put the progress information into the "a" molecules. For instance, the "a" molecule can carry the number of sums already performed as a second value. Let "a x,k " represent a partial sum of "k" elements, while the value of the partial sum is "x".
Initially we will inject "a x,1 " for each x from the given array.