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:
-
One PE can create and start a remote thread at another.
-
PEs can share global objects.
-
PEs can synchronize by Event objects which support reliable ordered
multicast.
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:
import aleph.*;
public class Hello {
The remote thread that will execute at each PE is defined as a static inner
class:
static class HelloThread extends RemoteThread {
public void run() {
System.out.println("Hello World from PE " +
PE.thisPE() +
" of " + PE.numPEs());
}
}
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
public static void main(String[] args) {
to be called when your program starts. Here, this method starts by creating
an instance of a remote thread object.
HelloThread thread = new HelloThread();
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 enumerate 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.)
for (Enumeration e = PE.allPEs(); e.hasMoreElements(); )
thread.start((PE) e.nextElement(), join);
Finally, the caller waits for all remote threads to finish.
The result of running this program should look something like this:
Hello world from PE[0] of 4
Hello world from PE[1] of 4
Hello world from PE[2] of 4
Hello world from PE[3] of 4
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:
static class HelloThread extends RemoteFunction{
public Object run() {
return "Hello world from
" + PE.thisPE() + " of " + PE.numPEs();
}
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.