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
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.)
for (Iterator iter = PE.allPEs(); iter.hasNext(); )
thread.start((PE) e.next(), 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.