The Non-Blocking Computation Request

There may be a situation when you would like your application to dispatch a computation to Jicos, but not wait, at that point, for it to complete. This can be done with the Hsp method: setComputation. From the Javadoc, it can be seen that this method returns a ResultId (without waiting for the computation to complete). The application can subsequently submit other computations this way. When the application finally needs the answers, it invokes the getResult method. This method, if necessary, will block, returning a Result object, which contains the 2 things associated with a computation:
  • a ResultId, which can be obtained with the getId method of Result
  • an Object that represents the returned value of the computation, which can be obtained with the getValue method of Result.
If a ComputeException occurs within any Task, the client session is terminated: The application must logout, then login to regain access to that Hosting Service Provider.
I will modify this policy, (only) if there is an actual Jicos user and application that requires it. While it is easy to conjure up such applications, spending a lot of time on something that never actually happens seems to me to be a poor use of my time.

The design of this part of the interface is informed by the work on Piranha [ 1, 2, 3 ].

Illustration

The example application below invokes setComputation within a for statement. Each setComputation is requesting a Fibonacci computation for a different value of n

After this sequence of setComputations is complete, the application enters another for statement that collects the answers, using the getResult method. The Result objects may arrive in any order.

The Application class for this is given below.

Application.java
Here is the output, when we request 5 successive Fibonacci numbers, starting with F(2).


F( 2 ) = 2
F( 3 ) = 3
F( 4 ) = 5
F( 5 ) = 8
F( 6 ) = 13

Applicability

When would one choose to use this approach? If, for example, one wants to dispatch 256 independent computations and wait for them to complete, this can be done without resorting to these methods. Rather, one designs a Task, root, that instantiates the 256 Task objects, which are processed concurrently by Jicos. The root Task returns only after all 256 instantiated Task objects are complete. However, if constructing the 256 Task objects requires access to data that the client regards as private (i.e., the client does not want Hosts to have access to this data), then this asynchronous approach may be useful. For example, computing the 256 independent computations concurrently may utilize the Hosts more fully than would be the case if the 256 computations were computed in sequence. Again, unless the construction of these computations requires access to private data, the concurrency can be obtained via the root task design mentioned above. 

Moreover, if construction of these computations does not require access to private data, then the "root task" approach can have more concurrency. If the client constructs n Task objects, the time to do this clearly is O( n ). However, if the construction of the Task objects is done on the Hosts, some parallelism can be achieved. For example, the root task can decompose into 2 subtasks, which in turn decompose into 2 subtasks, etc. until all the 256 tasks are constructed, using approximately O( log n ) time. That is, the process of constructing the 256 Task objects can be accomplished concurrently by the hosts, clearly an advantage over constructing them sequentially in the client.

What if constructing the amount of time to construct any one of these 256 tasks is small compared with the amount of time needed to complete the invocation of any of their execute methods? Would the concurrency argument above have less force? Yes, but Task objects should themselves not be too coarse-grained. What if there are much more than 256 Hosts available? In JICOS, Task objects should execute for a small amount of time to maximize parallelism. While Task execution time probably should not be less than 1 second, neither should it be very large, since that too reduces available parallelism.

Keeping it simple means not using the asynchronous dispatch facility unless it is needed.