/*
* *************************************************************************************
* 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;
import junit.framework.TestCase;
import com.espertech.esper.client.Configuration;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.support.bean.SupportBean;
import com.espertech.esper.support.bean.SupportMarketDataBean;
import com.espertech.esper.support.client.SupportConfigFactory;
import com.espertech.esper.support.util.SupportMTUpdateListener;
import java.util.TreeSet;
import java.util.concurrent.*;
/**
* Test for multithread-safety of setting and reading variables.
* <p>
* Assume we have 2 statements that set 3 variables, and one statement that selects variables:
* <p>
* <pre>on A as a set var1 = a.value, var2 = a.value, var3 = var3 + 1<pre>
* <pre>on B as a set var1 = b.value, var2 = b.value, var3 = var3 + 1<pre>
* <pre>select var1, var2 from C(id=threadid)<pre> (one per thread)
* <p>
* Result: If 4 threads send A and B events and assign a random value, then var1 and var2 should always be the same value
* both when selected in the select statement.
* In addition, the counter var3 should not miss a single value when posted to listeners of the set-statements.
* <p>
* Each thread sends for each loop one A, B and C event, and returns the result for all "var3" values for checking when done.
*/
public class TestMTVariables extends TestCase
{
private EPServiceProvider epService;
private SupportMTUpdateListener listenerSetOne;
private SupportMTUpdateListener listenerSetTwo;
public void setUp()
{
Configuration config = SupportConfigFactory.getConfiguration();
epService = EPServiceProviderManager.getDefaultProvider(config);
epService.initialize();
epService.getEPAdministrator().getConfiguration().addVariable("var1", Long.class, 0);
epService.getEPAdministrator().getConfiguration().addVariable("var2", Long.class, 0);
epService.getEPAdministrator().getConfiguration().addVariable("var3", Long.class, 0);
listenerSetOne = new SupportMTUpdateListener();
listenerSetTwo = new SupportMTUpdateListener();
String stmtSetOneText = "on " + SupportBean.class.getName() + " set var1=longPrimitive, var2=longPrimitive, var3=var3+1";
String stmtSetTwoText = "on " + SupportMarketDataBean.class.getName() + " set var1=volume, var2=volume, var3=var3+1";
epService.getEPAdministrator().createEPL(stmtSetOneText).addListener(listenerSetOne);
epService.getEPAdministrator().createEPL(stmtSetTwoText).addListener(listenerSetTwo);
}
protected void tearDown() throws Exception {
listenerSetOne = null;
listenerSetTwo = null;
}
public void testMTSetAtomicity() throws Exception
{
trySetAndReadAtomic(4, 2000);
}
public void testMTAtomicity() throws Exception
{
trySetAndReadAtomic(2, 10000);
}
private void trySetAndReadAtomic(int numThreads, int numRepeats) throws Exception
{
ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
Future future[] = new Future[numThreads];
for (int i = 0; i < numThreads; i++)
{
Callable callable = new VariableReadWriteCallable(i, epService, numRepeats);
future[i] = threadPool.submit(callable);
}
threadPool.shutdown();
threadPool.awaitTermination(10, TimeUnit.SECONDS);
for (int i = 0; i < numThreads; i++)
{
assertTrue((Boolean) future[i].get());
}
// Determine if we have all numbers for var3 and didn't skip one.
// Since "var3 = var3 + 1" is executed by multiple statements and threads we need to have
// this counter have all the values from 0 to N-1.
TreeSet<Long> var3Values = new TreeSet<Long>();
for (EventBean theEvent : listenerSetOne.getNewDataListFlattened())
{
var3Values.add((Long) theEvent.get("var3"));
}
for (EventBean theEvent : listenerSetTwo.getNewDataListFlattened())
{
var3Values.add((Long) theEvent.get("var3"));
}
assertEquals(numThreads * numRepeats, var3Values.size());
for (int i = 1; i < numThreads * numRepeats + 1; i++)
{
assertTrue(var3Values.contains((long)i));
}
}
}