/*
* *************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
* *************************************************************************************
*/
package com.espertech.esper.multithread.dispatchmodel;
import junit.framework.TestCase;
import com.espertech.esper.dispatch.DispatchService;
import com.espertech.esper.dispatch.DispatchServiceImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.List;
import java.util.Arrays;
import java.util.concurrent.*;
/**
* A model for testing multithreaded dispatches to listeners.
* <p>
* Each thread in a loop:
* Next producer invoke
* Producer generates next integer
* Producer sends int[] {num, 0}
*/
public class TestMTDispatch extends TestCase
{
private static final Log log = LogFactory.getLog(TestMTDispatch.class);
public void testSceneOne() throws Exception
{
DispatchService dispatchService = new DispatchServiceImpl();
DispatchListenerImpl listener = new DispatchListenerImpl();
//UpdateDispatchViewModel updateDispatchView = new UpdateDispatchViewNonConcurrentModel(dispatchService, listener);
UpdateDispatchViewOrderEnforcingModel updateDispatchView = new UpdateDispatchViewOrderEnforcingModel(dispatchService, listener);
int numThreads = 2;
int numActions = 10000;
int ratioDoubleAdd = 5;
// generates {(1,0),(2,0), (3,0)}
trySend(numThreads, numActions, ratioDoubleAdd, updateDispatchView, dispatchService);
// assert size
List<int[][]> result = listener.getReceived();
assertEquals(numActions * numThreads, result.size());
// analyze result
int[] nominals = new int[result.size()];
for (int i = 0; i < result.size(); i++)
{
int[][] entry = result.get(i);
//System.out.println("entry=" + print(entry));
nominals[i] = entry[0][0];
assertEquals("Order not correct: #" + i, (i + 1), nominals[i]);
// Assert last digits and nominals, i.e. (1, 0) (1, 1), (1, 2)
for (int j = 0; j < entry.length; j++)
{
assertEquals(nominals[i], entry[j][0]);
assertEquals(j, entry[j][1]);
}
}
}
private void trySend(int numThreads, int numCount, int ratioDoubleAdd, UpdateDispatchViewModel updateDispatchView, DispatchService dispatchService) throws Exception
{
// execute
ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
Future future[] = new Future[numThreads];
DispatchCallable callables[] = new DispatchCallable[numThreads];
DispatchProducer producer = new DispatchProducer(updateDispatchView);
for (int i = 0; i < numThreads; i++)
{
callables[i] = new DispatchCallable(producer, i, numCount, ratioDoubleAdd, updateDispatchView, dispatchService);
future[i] = threadPool.submit(callables[i]);
}
threadPool.shutdown();
threadPool.awaitTermination(10, TimeUnit.SECONDS);
for (int i = 0; i < numThreads; i++)
{
assertTrue((Boolean) future[i].get());
}
}
private String print(int[][] input)
{
StringBuilder buf = new StringBuilder();
for (int i = 0; i < input.length; i++)
{
buf.append(Arrays.toString(input[i]));
buf.append(",");
}
return buf.toString();
}
public static class DispatchCallable implements Callable
{
private static final Log log = LogFactory.getLog(DispatchCallable.class);
private DispatchProducer sharedProducer;
private final int threadNum;
private final int numActions;
private final int ratioDoubleAdd;
private final UpdateDispatchViewModel updateDispatchView;
private final DispatchService dispatchService;
public DispatchCallable(DispatchProducer sharedProducer, int threadNum, int numActions, int ratioDoubleAdd, UpdateDispatchViewModel updateDispatchView, DispatchService dispatchService)
{
this.sharedProducer = sharedProducer;
this.threadNum = threadNum;
this.numActions = numActions;
this.ratioDoubleAdd = ratioDoubleAdd;
this.updateDispatchView = updateDispatchView;
this.dispatchService = dispatchService;
}
public Object call() throws Exception
{
log.info(".call Thread " + Thread.currentThread().getId() + " starting");
for (int i = 0; i < numActions; i++)
{
if (i % 10000 == 1)
{
log.info(".call Thread " + Thread.currentThread().getId() + " at " + i);
}
int nominal = sharedProducer.next();
if (i % ratioDoubleAdd == 1)
{
updateDispatchView.add(new int[] {nominal, 1});
}
dispatchService.dispatch();
}
log.info(".call Thread " + Thread.currentThread().getId() + " done");
return true;
}
}
}