/*
* Distributed as part of c3p0 v.0.9.1.1
*
* Copyright (C) 2005 Machinery For Change, Inc.
*
* Author: Steve Waldman <swaldman@mchange.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1, as
* published by the Free Software Foundation.
*
* This software 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software; see the file LICENSE. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.mchange.v2.async;
import com.mchange.v2.log.*;
import com.mchange.v2.util.ResourceClosedException;
/**
* A class that provides for effecient asynchronous execution
* of multiple tasks that may block, but that do not contend
* for the same locks. The order in which tasks will be executed
* is not guaranteed.
*/
public class RoundRobinAsynchronousRunner implements AsynchronousRunner, Queuable
{
private final static MLogger logger = MLog.getLogger( RoundRobinAsynchronousRunner.class );
//MT: unchanging, individual elements are thread-safe
final RunnableQueue[] rqs;
//MT: protected by this' lock
int task_turn = 0;
//MT: protected by this' lock
int view_turn = 0;
public RoundRobinAsynchronousRunner( int num_threads, boolean daemon )
{
this.rqs = new RunnableQueue[ num_threads ];
for(int i = 0; i < num_threads; ++i)
rqs[i] = new CarefulRunnableQueue( daemon, false );
}
public synchronized void postRunnable(Runnable r)
{
try
{
int index = task_turn;
task_turn = (task_turn + 1) % rqs.length;
rqs[index].postRunnable( r );
/* we do this "long-hand" to avoid bad fragility if an exception */
/* occurs in postRunnable, causing the mod step of the original */
/* concise code to get skipped, and leading (if */
/* task_turn == rqs.length - 1 when the exception occurs) to an */
/* endless cascade of ArrayIndexOutOfBoundsExceptions. */
/* we might alternatively have just put the mod step into a */
/* finally block, but that's too fancy. */
/* thanks to Travis Reeder for reporting this problem. */
//rqs[task_turn++].postRunnable( r );
//task_turn %= rqs.length;
}
catch ( NullPointerException e )
{
//e.printStackTrace();
if ( Debug.DEBUG )
{
if ( logger.isLoggable( MLevel.FINE ) )
logger.log( MLevel.FINE, "NullPointerException while posting Runnable -- Probably we're closed.", e );
}
this.close( true );
throw new ResourceClosedException("Attempted to use a RoundRobinAsynchronousRunner in a closed or broken state.");
}
}
public synchronized RunnableQueue asRunnableQueue()
{
try
{
int index = view_turn;
view_turn = (view_turn + 1) % rqs.length;
return new RunnableQueueView( index );
/* same explanation as above */
//RunnableQueue out = new RunnableQueueView( view_turn++ );
//view_turn %= rqs.length;
//return out;
}
catch ( NullPointerException e )
{
//e.printStackTrace();
if ( Debug.DEBUG )
{
if ( logger.isLoggable( MLevel.FINE ) )
logger.log( MLevel.FINE, "NullPointerException in asRunnableQueue() -- Probably we're closed.", e );
}
this.close( true );
throw new ResourceClosedException("Attempted to use a RoundRobinAsynchronousRunner in a closed or broken state.");
}
}
public synchronized void close( boolean skip_remaining_tasks )
{
for (int i = 0, len = rqs.length; i < len; ++i)
{
attemptClose( rqs[i], skip_remaining_tasks );
rqs[i] = null;
}
}
public void close()
{ close( true ); }
static void attemptClose(RunnableQueue rq, boolean skip_remaining_tasks)
{
try { rq.close( skip_remaining_tasks ); }
catch ( Exception e )
{
//e.printStackTrace();
if ( logger.isLoggable( MLevel.WARNING ) )
logger.log( MLevel.WARNING, "RunnableQueue close FAILED.", e );
}
}
class RunnableQueueView implements RunnableQueue
{
final int rq_num;
RunnableQueueView( int rq_num )
{ this.rq_num = rq_num; }
public void postRunnable(Runnable r)
{ rqs[ rq_num ].postRunnable( r ); }
public void close( boolean skip_remaining_tasks )
{ }
public void close()
{ /* ignore */ }
}
}