/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.util;

import java.util.Vector;

/**
 * A class which takes any number of <CODE>Runnable</CODE> objects, and executes them in parallel on
 * multiple threads insofar as possible.
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 * @see java.lang.Runnable
 * @see java.lang.Thread
 */
public class ParallelRunQueue implements Runnable
{
  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private Thread[] thrds;    // the current threads
  private Vector queue;      // the queue of Runnables to be run
  private int priority;      // the priority to use for all these threads

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  /**
   * Creates a new <CODE>ParallelRunQueue</CODE>.
   *
   * @param nthread Number of threads to be executed in parallel with the current one by this run queue.
   */
  public ParallelRunQueue(int nthread)
  {
    thrds = new Thread[nthread];
    for (int i=0; i<nthread; i++)
      thrds[i] = null;  // no threads to start with
    queue = new Vector();
    priority = Thread.currentThread().getPriority();  // default the priority

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface Runnable
   *--------------------------------------------------------------------------------
   */

  /**
   * Executes all <CODE>Runnable</CODE> objects currently queued.  When this method returns, the run queue
   * is empty.  The objects are executed on one of the internal worker threads, or on the current thread
   * if no other threads are available.
   */
  public void run()
  {
    while (queue.size()>0)
    { // unqueue a new Runnable
      Runnable r = (Runnable)(queue.remove(0));
      for (int i=0; i<thrds.length; i++)
      { // scan through our threads list...
	if ((thrds[i]==null) || !(thrds[i].isAlive()))
	{ // a worker thread is available - start it running
	  thrds[i] = new Thread(r);
	  thrds[i].setPriority(priority);
	  thrds[i].start();
	  return;

	} // end if

      } // end for

      r.run();  // if all else fails, run it ourselves

    } // end while

    work();  // GC any further dead threads

  } // end run

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Enqueues a new <CODE>Runnable</CODE> object onto the queue.  If a worker thread is available, the
   * new object is started running immediately.
   *
   * @param r The <CODE>Runnable</CODE> object to enqueue.
   */
  public void queue(Runnable r)
  {
    for (int i=0; i<thrds.length; i++)
    { // scan through our threads list...
      if ((thrds[i]==null) || !(thrds[i].isAlive()))
      { // a worker thread is available - start it running
	thrds[i] = new Thread(r);
	thrds[i].setPriority(priority);
	thrds[i].start();
	work();
	return;

      } // end if

    } // end for

    queue.add(r);  // if all else fails, queue it up

  } // end queue

  /**
   * If any threads in the run queue are dead, replaces them with live threads running fresh elements out
   * of the queue.  If no more <CODE>Runnable</CODE> objects remain, then dead threads are nulled out to
   * garbage-collect them.
   */
  public void work()
  {
    for (int i=0; i<thrds.length; i++)
      if ((thrds[i]==null) || !(thrds[i].isAlive()))
      { // found an empty slot or dead thread to replace
	if (queue.size()==0)
	  thrds[i] = null;  // let the dead thread be garbage-collected
	else
	{ // get the next Runnable and enqueue it
	  Runnable r = (Runnable)(queue.remove(0));
	  thrds[i] = new Thread(r);
	  thrds[i].setPriority(priority);
	  thrds[i].start();

	} // end else

      } // end if and for

  } // end work

} // end class ParallelRunQueue
