View Javadoc
1   //******************************************************************************
2   //
3   // File:    ParallelIteration.java
4   // Package: edu.rit.pj
5   // Unit:    Class edu.rit.pj.ParallelIteration
6   //
7   // This Java source file is copyright (C) 2007 by Alan Kaminsky. All rights
8   // reserved. For further information, contact the author, Alan Kaminsky, at
9   // ark@cs.rit.edu.
10  //
11  // This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
12  // software; you can redistribute it and/or modify it under the terms of the GNU
13  // General Public License as published by the Free Software Foundation; either
14  // version 3 of the License, or (at your option) any later version.
15  //
16  // PJ is distributed in the hope that it will be useful, but WITHOUT ANY
17  // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18  // A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19  //
20  // Linking this library statically or dynamically with other modules is making a
21  // combined work based on this library. Thus, the terms and conditions of the GNU
22  // General Public License cover the whole combination.
23  //
24  // As a special exception, the copyright holders of this library give you
25  // permission to link this library with independent modules to produce an
26  // executable, regardless of the license terms of these independent modules, and
27  // to copy and distribute the resulting executable under terms of your choice,
28  // provided that you also meet, for each linked independent module, the terms
29  // and conditions of the license of that module. An independent module is a module
30  // which is not derived from or based on this library. If you modify this library,
31  // you may extend this exception to your version of the library, but you are not
32  // obligated to do so. If you do not wish to do so, delete this exception
33  // statement from your version.
34  //
35  // A copy of the GNU General Public License is provided in the file gpl.txt. You
36  // may also obtain a copy of the GNU General Public License on the World Wide
37  // Web at http://www.gnu.org/licenses/gpl.html.
38  //
39  //******************************************************************************
40  package edu.rit.pj;
41  
42  /**
43   * Class ParallelIteration is the abstract base class for a parallel iteration
44   * that is executed inside a {@linkplain ParallelRegion}. The parallel iteration
45   * lets you iterate over a group of items, with a separate parallel team thread
46   * processing each item. The generic type parameter T specifies the items' data
47   * type. The items can be the elements of an array, the items obtained from an
48   * {@linkplain java.util.Iterator Iterator}, or the items contained in an
49   * {@linkplain java.lang.Iterable Iterable} collection.
50   * <P>
51   * To execute a parallel iteration, create a {@linkplain ParallelRegion} object;
52   * create an instance of a concrete subclass of class ParallelIteration; and
53   * pass this instance to the parallel region's <code>execute()</code> method. Either
54   * every parallel team thread must call the parallel region's <code>execute()</code>
55   * method with identical arguments, or every thread must not call the
56   * <code>execute()</code> method. You can do all this using an anonymous inner
57   * class; for example:
58   * <PRE>
59   *     new ParallelRegion()
60   *         {
61   *         ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;();
62   *         . . .
63   *         public void run()
64   *             {
65   *             . . .
66   *             execute (list, new ParallelIteration&lt;String&gt;()
67   *                 {
68   *                 // Thread local variable declarations
69   *                 . . .
70   *                 public void start()
71   *                     {
72   *                     // Per-thread pre-loop initialization code
73   *                     . . .
74   *                     }
75   *                 public void run (String item)
76   *                     {
77   *                     // Loop body code
78   *                     . . .
79   *                     }
80   *                 public void finish()
81   *                     {
82   *                     // Per-thread post-loop finalization code
83   *                     . . .
84   *                     }
85   *                 });
86   *             }
87   *         . . .
88   *         }
89   * </PRE>
90   * <P>
91   * The parallel region's <code>execute()</code> method does the following. One of
92   * the parallel team threads sets up the source of the items to be iterated over
93   * -- either an array's elements, an iterator's items, or an iterable
94   * collection's contents. (Note that only <I>one</I> thread does this setup; but
95   * because all threads must call the parallel region's <code>execute()</code> method
96   * with identical arguments, it doesn't matter which thread does the setup.)
97   * Each parallel team thread calls the parallel iteration's <code>start()</code>
98   * method once before beginning any loop iterations. Each thread repeatedly
99   * calls the parallel iteration's <code>run()</code> method, passing in a different
100  * item on each call, until all the items have been processed. When a thread has
101  * finished calling <code>run()</code>, the thread calls the parallel iteration's
102  * <code>finish()</code> method. Then the thread waits at a barrier. When all the
103  * threads have reached the barrier, the <code>execute()</code> method returns.
104  * <P>
105  * Note that each parallel team thread actually creates its own instance of the
106  * parallel iteration class and passes that instance to the parallel region's
107  * <code>execute()</code> method. Thus, any fields declared in the parallel
108  * iteration class will <I>not</I> be shared by all the threads, but instead
109  * will be private to each thread.
110  * <P>
111  * The <code>start()</code> method is intended for performing per-thread
112  * initialization before starting the loop iterations. If no such initialization
113  * is needed, omit the <code>start()</code> method.
114  * <P>
115  * The <code>run()</code> method contains the code for the loop body. It does
116  * whatever processing is needed on the one item passed in as an argument. Note
117  * that, unlike a parallel for loop (class {@linkplain ParallelForLoop}), a
118  * parallel iteration is not "chunked;" each parallel team thread always
119  * processes just one item at a time.
120  * <P>
121  * The <code>finish()</code> method is intended for performing per-thread
122  * finalization after finishing the loop iterations. If no such finalization is
123  * needed, omit the <code>finish()</code> method.
124  * <P>
125  * Sometimes a portion of a parallel iteration has to be executed sequentially
126  * in the same order as the items' iteration order, while the rest of the
127  * parallel iteration can be executed concurrently. For example, the loop body
128  * is performing some computation that can be executed in parallel for different
129  * items, but the results of each computation must be written to a file
130  * sequentially in the items' iteration order. The <code>ordered()</code> method is
131  * provided for this purpose. A call to the <code>ordered()</code> method may appear
132  * once in the parallel iteration's <code>run()</code> method, like so:
133  * <PRE>
134  *     public void run (String item)
135  *         {
136  *         // This portion executed concurrently
137  *         . . .
138  *         ordered (new ParallelSection()
139  *             {
140  *             public void run()
141  *                 {
142  *                 // This portion executed sequentially
143  *                 // in the items' iteration order
144  *                 . . .
145  *                 }
146  *             });
147  *         // This portion executed concurrently again
148  *         . . .
149  *         }
150  * </PRE> When called, the <code>ordered()</code> method waits until the
151  * <code>ordered()</code>
152  * method has been called and has returned for all items prior to the current
153  * item. Then the <code>ordered()</code> method calls the given parallel section's
154  * <code>run()</code> method. When the parallel section's <code>run()</code> method
155  * returns, the <code>ordered()</code> method returns. If the parallel section's
156  * <code>run()</code> method throws an exception, the <code>ordered()</code> method
157  * throws that same exception.
158  * <P>
159  * It is possible to stop a parallel iteration using the <code>stopLoop()</code>
160  * method, like this:
161  * <PRE>
162  *     public void run (String item)
163  *         {
164  *         // Loop body
165  *         . . .
166  *         if (/&#42;time to stop the loop&#42;/)
167  *             {
168  *             stopLoop();
169  *             return;
170  *             }
171  *         // More loop body
172  *         . . .
173  *         }
174  * </PRE> Once <code>stopLoop()</code> is called, after each parallel team thread
175  * finishes processing its current item, each thread will process no further
176  * items and will proceed to finish the parallel iteration. Note well that
177  * stopping a parallel iteration is not the same as executing a <code>break</code>
178  * statement in a regular loop. The parallel iteration does not stop until each
179  * thread,
180  * <I>including the thread that called <code>stopLoop()</code></I>, has finished
181  * processing its current item. Thus, processing may continue for a while after
182  * <code>stopLoop()</code> is called. (The <code>return</code> statement in the above
183  * example causes the thread that called <code>stopLoop()</code> to stop its
184  * processing early.)
185  * <P>
186  * Normally, at the end of the parallel iteration, the parallel team threads
187  * wait for each other at a barrier. To eliminate this barrier wait, include
188  * {@link edu.rit.pj.BarrierAction#NO_WAIT BarrierAction.NO_WAIT} in the <code>execute()</code>
189  * method call:
190  * <PRE>
191  *     new ParallelRegion()
192  *         {
193  *         . . .
194  *         public void run()
195  *             {
196  *             . . .
197  *             execute (list, new ParallelIteration&lt;String&gt;()
198  *                 {
199  *                 . . .
200  *                 },
201  *             BarrierAction.NO_WAIT);
202  *             . . .
203  *             }
204  *         }
205  * </PRE> To execute a section of code in a single thread as part of the barrier
206  * synchronization, include an instance of class {@linkplain BarrierAction} in
207  * the <code>execute()</code> method call. The barrier action object's
208  * <code>run()</code> method contains the code to be executed in a single thread
209  * while the other threads wait:
210  * <PRE>
211  *     new ParallelRegion()
212  *         {
213  *         . . .
214  *         public void run()
215  *             {
216  *             . . .
217  *             execute (list, new ParallelIteration&lt;String&gt;()
218  *                 {
219  *                 . . .
220  *                 },
221  *             new BarrierAction()
222  *                 {
223  *                 public void run()
224  *                     {
225  *                     // Single-threaded code goes here
226  *                     . . .
227  *                     }
228  *                 });
229  *             . . .
230  *             }
231  *         }
232  * </PRE> For further information, see class {@linkplain BarrierAction}.
233  * <P>
234  * If the parallel iteration's <code>start()</code>, <code>run()</code>, or
235  * <code>finish()</code> method throws an exception in one of the threads, then that
236  * thread executes no further code in the loop, and the parallel region's
237  * <code>execute()</code> method throws that same exception in that thread.
238  * Furthermore, the other threads in the parallel team also process no further
239  * items after finishing their current items. Thus, if one thread throws an
240  * exception, the whole parallel iteration exits with some (perhaps none) of the
241  * iterations unperformed.
242  *
243  * @param <T> Data type of the items iterated over.
244  * @author Alan Kaminsky
245  * @version 11-Nov-2007
246  */
247 public abstract class ParallelIteration<T>
248         extends ParallelConstruct {
249 
250 // Hidden data members.
251     // Item generator, used to obtain the items, to break this parallel
252     // iteration, and to implement the ordered() construct.
253     ItemGenerator<T> myItemGenerator;
254 
255     // Iteration index for the ordered() construct.
256     int myOrderedIndex;
257 
258 // Exported constructors.
259     /**
260      * Construct a new parallel iteration.
261      */
262     public ParallelIteration() {
263         super();
264     }
265 
266 // Exported operations.
267     /**
268      * Perform per-thread initialization actions before starting the loop
269      * iterations.
270      * <P>
271      * The <code>start()</code> method may be overridden in a subclass. If not
272      * overridden, the <code>start()</code> method does nothing.
273      *
274      * @exception Exception The <code>start()</code> method may throw any exception.
275      * @throws java.lang.Exception if any.
276      */
277     public void start()
278             throws Exception {
279     }
280 
281     /**
282      * Process one item in this parallel iteration. The <code>run()</code> method
283      * must perform the loop body for the given item.
284      * <P>
285      * The <code>run()</code> method must be overridden in a subclass.
286      *
287      * @param item Item.
288      * @exception Exception The <code>run()</code> method may throw any exception.
289      * @throws java.lang.Exception if any.
290      */
291     public abstract void run(T item)
292             throws Exception;
293 
294     /**
295      * Perform per-thread finalization actions after finishing the loop
296      * iterations.
297      * <P>
298      * The <code>finish()</code> method may be overridden in a subclass. If not
299      * overridden, the <code>finish()</code> method does nothing.
300      *
301      * @exception Exception The <code>finish()</code> method may throw any
302      * exception.
303      * @throws java.lang.Exception if any.
304      */
305     public void finish()
306             throws Exception {
307     }
308 
309     /**
310      * Execute the given section of code in the items' iteration order. A call
311      * to the <code>ordered()</code> method may appear in this parallel iteration's
312      * <code>run()</code> method. When called, the <code>ordered()</code> method waits
313      * until the <code>ordered()</code> method has been called and has returned for
314      * all items prior to the current item. Then the <code>ordered()</code> method
315      * calls the <code>run()</code> method of <code>theParallelSection</code>. When the
316      * parallel section's <code>run()</code> method returns, the <code>ordered()</code>
317      * method returns. If the parallel section's <code>run()</code> method throws an
318      * exception, the <code>ordered()</code> method throws that same exception.
319      * <P>
320      * The <code>ordered()</code> method is used when a portion of a parallel
321      * iteration has to be executed sequentially in the items' iteration order,
322      * while the rest of the parallel iteration can be executed concurrently.
323      * <P>
324      * <I>Note:</I> Either the <code>ordered()</code> method must be called exactly
325      * once during each call of the parallel iteration's <code>run()</code> method,
326      * or the <code>ordered()</code> method must not be called at all.
327      *
328      * @param theSection Parallel section to execute in order.
329      * @exception NullPointerException (unchecked exception) Thrown if
330      * <code>theSection</code> is null.
331      * @exception IllegalStateException (unchecked exception) Thrown if no
332      * parallel team is executing this parallel iteration.
333      * @exception Exception Thrown if <code>theSection</code>'s <code>run()</code>
334      * method throws an exception.
335      * @throws java.lang.Exception if any.
336      */
337     public final void ordered(ParallelSection theSection)
338             throws Exception {
339         // Verify preconditions.
340         if (theSection == null) {
341             throw new IllegalStateException("ParallelIteration.ordered(): Parallel section is null");
342         }
343         if (myTeam == null) {
344             throw new IllegalStateException("ParallelIteration.ordered(): No parallel team executing");
345         }
346 
347         // Wait until the ordered() construct has finished for all previous
348         // iterations.
349         if (myItemGenerator.myOrderedIndex != this.myOrderedIndex) {
350             Spinner spinner = new Spinner();
351             while (myItemGenerator.myOrderedIndex != this.myOrderedIndex) {
352                 spinner.spin();
353             }
354         }
355 
356         // Execute parallel section. Propagate any exception.
357         theSection.myTeam = this.myTeam;
358         try {
359             theSection.run();
360         } finally {
361             theSection.myTeam = null;
362 
363             // Notify that the ordered construct has finished for this
364             // iteration.
365             ++this.myOrderedIndex;
366             myItemGenerator.myOrderedIndex = this.myOrderedIndex;
367         }
368     }
369 
370     /**
371      * Stop this parallel iteration. Once <code>stopLoop()</code> is called, after
372      * each parallel team thread finishes processing its current item, each
373      * thread will process no further items and will proceed to finish this
374      * parallel iteration.
375      *
376      * @exception IllegalStateException (unchecked exception) Thrown if no
377      * parallel team is executing this parallel iteration.
378      */
379     public final void stopLoop() {
380         if (myTeam == null) {
381             throw new IllegalStateException("ParallelIteration.stopLoop(): No parallel team executing");
382         }
383         myItemGenerator.myBreak = true;
384     }
385 
386 // Hidden operations.
387     /**
388      * Execute one chunk of iterations of this parallel for loop. This method
389      * performs common processing, then calls the <code>run()</code> method.
390      *
391      * @param index Iteration index.
392      * @param item Item.
393      *
394      * @exception Exception This method may throw any exception.
395      */
396     void commonRun(int index,
397             T item)
398             throws Exception {
399         myOrderedIndex = index;
400         run(item);
401     }
402 
403 }