//******************************************************************************
//
// File: WorkerIteration.java
// Package: edu.rit.pj
// Unit: Class edu.rit.pj.WorkerIteration
//
// This Java source file is copyright (C) 2010 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.pj;
import edu.rit.mp.ObjectBuf;
import edu.rit.mp.buf.ObjectItemBuf;
import edu.rit.util.Range;
import java.io.IOException;
/**
* Class WorkerIteration is the abstract base class for a worker iteration that
* is executed inside a {@linkplain WorkerRegion}. The worker iteration lets you
* iterate over a group of items, with a separate worker team thread processing
* each item. The generic type parameter T specifies the items' data type. The
* items can be the elements of an array, the items obtained from an {@linkplain
* java.util.Iterator Iterator}, or the items contained in an {@linkplain
* java.lang.Iterable Iterable} collection.
*
* To execute a worker iteration, create a {@linkplain WorkerRegion} object;
* create an instance of a concrete subclass of class WorkerIteration; and pass
* this instance to the worker region's execute() method. Either every
* worker team thread must call the worker region's execute() method
* with identical arguments, or every thread must not call the
* execute() method. You can do all this using an anonymous inner
* class; for example:
*
* new WorkerRegion()
* {
* ArrayList<String> list = new ArrayList<String>();
* . . .
* public void run()
* {
* . . .
* execute (list, new WorkerIteration<String>()
* {
* // Thread local variable declarations
* . . .
* public void start()
* {
* // Per-thread pre-loop initialization code
* . . .
* }
* public void run (String item)
* {
* // Loop code
* . . .
* }
* public void finish()
* {
* // Per-thread post-loop finalization code
* . . .
* }
* });
* }
* . . .
* }
*
*
* In each process of a cluster parallel program, the worker team has one or
* more worker threads. Every worker thread in every process has a unique worker
* index, going from index 0 for the first worker thread in the first process to
* index K−1 for the last worker thread in the last process, where
* K is the total number of worker threads in all the processes. In
* addition, in one process there is a master thread. The worker and master
* threads all call the worker region's execute() method to execute the
* worker for loop. However, the worker and master threads differ in their
* actions.
*
* The master thread does the following. The master sets up the source of items
* to be iterated over -- either an array's elements, an iterator's items, or an
* iterable collection's contents. The master repeatedly sends "tasks" to the
* workers and receives "responses" from the workers. To send a task to a
* particular worker, the master (1) sends a message containing the next item to
* the worker's process; and (2) calls the worker iteration's
* sendTaskInput() method. This method's default implementation does
* nothing, but it can be overridden to send additional task input data to the
* worker. To receive a response from a particular worker, the master (1)
* receives a message from the worker's process containing the item that was
* processed (whose state may have changed); and (2) calls the worker
* iteration's receiveTaskOutput() method. This method's default
* implementation does nothing, but it can be overridden to receive additional
* task output data from the worker. Once all tasks have been sent to the
* workers and all responses have been received from the workers, the master
* returns from the worker region's execute() method.
*
* Each worker thread does the following. The worker calls the worker
* iteration's start() method once before processing any items. The
* worker repeatedly receives tasks from the master and sends responses to the
* master. To receive a task from the master, the worker (1) receives a message
* containing the next item from the master's process; and (2) calls the worker
* iteration's receiveTaskInput() method. This method's default
* implementation does nothing, but it can be overridden to receive additional
* task input data from the master. The worker now calls the worker iteration's
* run() method, passing in the item to be processed. When the
* run() method returns, the worker sends the response to the master.
* To send the response, the worker (1) sends a message to the master's process
* containing the item that was processed (whose state may have changed); and
* (2) calls the worker iteration's sendTaskOutput() method. This
* method's default implementation does nothing, but it can be overridden to
* send additional task output data to the master. Once all tasks have been
* received from the master and all responses have been sent to the master, the
* worker calls the worker iteration's finish() method. (Unlike a
* {@linkplain ParallelIteration}'s threads, the workers do not
* synchronize with each other at a barrier at this point.) The worker then
* returns from the worker region's execute() method.
*
* Each message described above is sent with a message tag equal to
* W+T, where W is the worker index and T is the
* "tag offset." The tag offset is Integer.MIN_VALUE by default, but
* this can be changed by overriding the tagOffset() method. Thus, the
* message tags fall in the range T .. K−1+T, where
* K is the total number of workers in all the processes. The program
* should not use message tags in this range except to send and receive the
* messages described above.
*
* Note that each worker team thread actually creates its own instance of the
* worker iteration class and passes that instance to the worker region's
* execute() method. Thus, any fields declared in the worker iteration
* class will not be shared by all the workers, but instead will be
* private to each worker.
*
* The start() method is intended for performing per-thread
* initialization before starting the loop iterations. If no such initialization
* is needed, omit the start() method.
*
* The run() method contains the code for the loop body. It does
* whatever processing is needed on the one item passed in as an argument. Note
* that, unlike a worker for loop (class {@linkplain WorkerForLoop}), a worker
* iteration is not "chunked;" each worker team thread always processes just one
* item at a time.
*
* The finish() method is intended for performing per-thread
* finalization after finishing the loop iterations. If no such finalization is
* needed, omit the finish() method.
*
* If the worker iteration's start(), run(), or
* finish() method throws an exception in one of the worker threads,
* then that worker thread executes no further code in the loop, and the worker
* region's execute() method throws that same exception in that thread.
* However, the other worker threads in the worker team continue to execute.
*
* @param Data type of the items iterated over.
*
* @author Alan Kaminsky
* @version 07-Oct-2010
*/
public abstract class WorkerIteration
extends WorkerConstruct
{
// Exported constructors.
/**
* Construct a new worker iteration.
*/
public WorkerIteration()
{
super();
}
// Exported operations.
/**
* Perform per-thread initialization actions before starting the loop
* iterations. Called by a worker thread.
*
* The start() method may be overridden in a subclass. If not
* overridden, the start() method does nothing.
*
* @exception Exception
* The start() method may throw any exception.
*/
public void start()
throws Exception
{
}
/**
* Send additional input data associated with a task. Called by the master
* thread. The task is denoted by the given item to be processed. The input
* data must be sent using the given communicator, to the given worker
* process rank, with the given message tag.
*
* The sendTaskInput() method may be overridden in a subclass. If
* not overridden, the sendTaskInput() method does nothing.
*
* @param item Item to be processed.
* @param comm Communicator.
* @param wRank Worker process rank.
* @param tag Message tag.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void sendTaskInput
(T item,
Comm comm,
int wRank,
int tag)
throws IOException
{
}
/**
* Receive input data associated with a task. Called by a worker thread. The
* task is denoted by the given item to be processed. The input data must be
* received using the given communicator, from the given master process
* rank, with the given message tag.
*
* The receiveTaskInput() method may be overridden in a subclass.
* If not overridden, the receiveTaskInput() method does nothing.
*
* @param item Item to be processed.
* @param comm Communicator.
* @param mRank Master process rank.
* @param tag Message tag.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void receiveTaskInput
(T item,
Comm comm,
int mRank,
int tag)
throws IOException
{
}
/**
* Process one item in this worker iteration. The run() method must
* perform the loop body for the given item.
*
* The run() method must be overridden in a subclass.
*
* @param item Item.
*
* @exception Exception
* The run() method may throw any exception.
*/
public abstract void run
(T item)
throws Exception;
/**
* Send additional output data associated with a task. Called by a worker
* thread. The task is denoted by the given item that was processed (whose
* state may have changed during processing). The output data must be sent
* using the given communicator, to the given master process rank, with the
* given message tag.
*
* The sendTaskOutput() method may be overridden in a subclass. If
* not overridden, the sendTaskOutput() method does nothing.
*
* @param item Item that was processed.
* @param comm Communicator.
* @param mRank Master process rank.
* @param tag Message tag.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void sendTaskOutput
(T item,
Comm comm,
int mRank,
int tag)
throws IOException
{
}
/**
* Receive additional output data associated with a task. Called by the
* master thread. The task is denoted by the given item that was processed
* (whose state may have changed during processing). The output data must be
* received using the given communicator, from the given worker process
* rank, with the given message tag.
*
* The receiveTaskOutput() method may be overridden in a subclass.
* If not overridden, the receiveTaskOutput() method does nothing.
*
* @param item Item that was processed.
* @param comm Communicator.
* @param wRank Worker process rank.
* @param tag Message tag.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
public void receiveTaskOutput
(T item,
Comm comm,
int wRank,
int tag)
throws IOException
{
}
/**
* Perform per-thread finalization actions after finishing the loop
* iterations. Called by a worker thread.
*
* The finish() method may be overridden in a subclass. If not
* overridden, the finish() method does nothing.
*
* @exception Exception
* The finish() method may throw any exception.
*/
public void finish()
throws Exception
{
}
/**
* Returns the tag offset for this worker for loop. Each message between the
* master and worker threads is sent with a message tag equal to
* W+T, where W is the worker index and T is the
* tag offset.
*
* The tagOffset() method may be overridden in a subclass. If not
* overridden, the tagOffset() returns a default tag offset of
* Integer.MIN_VALUE.
*
* @return Tag offset.
*/
public int tagOffset()
{
return Integer.MIN_VALUE;
}
// Hidden operations.
/**
* Execute this worker iteration in the master thread.
*
* @param generator Item generator.
*
* @exception IOException
* Thrown if an I/O error occurred.
*/
void masterExecute
(ItemGenerator generator)
throws IOException
{
int count = myTeam.count;
int remaining = count;
ObjectItemBuf> buf = ObjectBuf.buffer();
Range tagRange = new Range (tagFor (0), tagFor (count - 1));
Comm comm = myTeam.comm;
// Send initial task to each worker.
for (int w = 0; w < count; ++ w)
{
ItemHolder holder = generator.nextItem();
buf.item = holder;
buf.reset();
int r = myTeam.workerRank (w);
int tag = tagFor (w);
comm.send (r, tag, buf);
if (holder == null)
{
-- remaining;
}
else
{
sendTaskInput (holder.myItem, comm, r, tag);
}
}
// Repeatedly receive a response from a worker and send next task to
// that worker.
while (remaining > 0)
{
CommStatus status = comm.receive (null, tagRange, buf);
ItemHolder holder = buf.item;
int r = status.fromRank;
int tag = status.tag;
int w = workerFor (tag);
receiveTaskOutput (holder.myItem, comm, r, tag);
holder = generator.nextItem();
buf.item = holder;
buf.reset();
comm.send (r, tag, buf);
if (holder == null)
{
-- remaining;
}
else
{
sendTaskInput (holder.myItem, comm, r, tag);
}
}
}
/**
* Execute this worker for loop in a worker thread.
*
* @param w Worker index.
*
* @exception Exception
* This method may throw any exception.
*/
void workerExecute
(int w)
throws Exception
{
Comm comm = myTeam.comm;
int r = myTeam.masterRank();
int tag = tagFor (w);
start();
ObjectItemBuf> buf = ObjectBuf.buffer();
for (;;)
{
comm.receive (r, tag, buf);
ItemHolder holder = buf.item;
if (holder == null) break;
receiveTaskInput (holder.myItem, comm, r, tag);
run (holder.myItem);
buf.reset();
// The next two statements constitute a critical section; other
// workers in this team must not send messages in between these two
// messages, or the master can deadlock.
synchronized (myTeam)
{
comm.send (r, tag, buf);
sendTaskOutput (holder.myItem, comm, r, tag);
}
}
finish();
}
/**
* Returns the message tag for the given worker index.
*
* @param w Worker index.
*
* @return Message tag.
*/
private int tagFor
(int w)
{
return w + tagOffset();
}
/**
* Returns the worker index for the given message tag.
*
* @param tag Message tag.
*
* @return Worker index.
*/
private int workerFor
(int tag)
{
return tag - tagOffset();
}
}
Ads help maintain this website.