Aleph Toolkit Tutorial

This document is an introduction to using the Aleph Toolkit, a collection of Javatm packages implementing a simple, platform-independent distributed shared object system. It does not describe the mechanics of how to install or run the toolkit, which may vary from site to site. For that kind of information, please see the User's Guide.

A toolkit client is a distributed program that runs on a number of logical processors, called Processing Elements (PEs). Each PE is a Java Virtual Machine, with its own address space. PEs communicate in various ways:

Hello World

To illustrate how remote threads work, we will construct a Hello class implementing the customary Hello World application. The first step is to make the Aleph API available to the program: The remote thread that will execute at each PE is defined as a static inner class: Every remote thread must extend the abstract class aleph.RemoteThread. Like regular Java threads (java.lang.Thread), the class must provide a public void run() method to be called when the thread is started. The aleph.PE class manages processing elements (PEs). Here, PE.thisPE() returns the PE where the program is running, and PE.numPEs() returns the total number of PEs.

As usual for Java programs, your class must include a method with signature

to be called when your program starts. Here, this method starts by creating an instance of a remote thread object. As with regular threads, a remote thread does not start executing until it is explicitly started. Next, the program creates an aleph.Join object to allow the caller to wait until the remote threads have completed: The method then executes a for loop , which uses PE.allPEs() to iterate over all PEs. The loop body starts an instance of the remote thread at each PE. (Unlike java.lang.Thread, it is possible to start a single RemoteThread instance multiple times.) Finally, the caller waits for all remote threads to finish. The result of running this program should look something like this: Another way to write this program is to use a RemoteFunction , which is a remote thread that returns a result (essentially a remote procedure call).  Here, the remote function must define a run method that returns a value: The caller collects the responses by iterating over a join object:

    while(join.hasMoreElements())
      System.out.println((String) join.nextElement());

Global Objects

PEs can also share data structures called global objects, defined by the GlobalObject interface. To illustrate how global objects work, we will construct a Counter class implementing a simple shared counter application. A global object is a container for a regular Java object. In this case, the counter is defined as a class:

     public class Counter implements java.io.Serializable {
    public long value;

All objects shared by PEs must implement java.io.Serializable. The main method for the class simply encapsulates the counter in a GlobalObject as follows:

  GlobalObject global = new GlobalObject(new Counter());

This object is passed as an argument to the constructor for the user's threads:

  public UserThread(GlobalObject global) {

Each user thread access the counter by opening the global object in ``write'' mode:

  Counter counter = (Counter) global.open("w");

The cast is necessary because open returns an object of class java.lang.object, and the ``w'' indicates that the object is to be accessed in write mode. Currently supported modes are read "r" and write "w". In read mode, the object is up-to-date, but should not be modified. In write mode, the object is up-to-date, and may be modified. Note that thread-level synchronization may be needed to support concurrent write and copy accesses.

Once the counter is open, it can be modified and released.

  Counter.value++;
  global.release();

The Aleph toolkit also supports transactions running at a single PE. Here is a simple example:

  Transaction t = new Transaction();
  Counter counter = (Counter) global.open(t, "w");
  if (t.commit())
    System.out.println("Counter incremented");
  else
    System.out.println("Counter increment failed");

For the time being, transactions must execute entirely within a single PE.

Events

 Events provide a simple and flexible way for threads in different PEs to synchronize. An Event object is a kind of global object: it can be shared among multiple PEs.

 When a thread calls an Event object's signal method, then that signal is (conceptually, at least) broadcast to all other PEs, together with a copy of the (optional) argument to the call.

If a PE wants to be notified that an event has been signaled, it registers a Listener object with that event. Just as in the Abstract Window Toolkit (awt), the listener is a dummy object that provides a void actionPerformed(Object object) method that is called when the event is signaled. The argument to the method is the argument to signal, or null if there was no argument. Signals are delivered in order, and each signal is delivered only once to each PE.

For an example of how to use events, see the implementation of the Barrier  class.