/*
* JBoss, Home of Professional Open Source
* Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* 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; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.messaging.tests.integration.asyncio;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.messaging.core.asyncio.AIOCallback;
import org.jboss.messaging.core.asyncio.impl.AsynchronousFileImpl;
import org.jboss.messaging.core.logging.Logger;
/**
*
* you need to define -Djava.library.path=${project-root}/native/src/.libs when calling the JVM
* If you are running this test in eclipse you should do:
* I - Run->Open Run Dialog
* II - Find the class on the list (you will find it if you already tried running this testcase before)
* III - Add -Djava.library.path=<your project place>/native/src/.libs
*
* @author <a href="mailto:clebert.suconic@jboss.com">Clebert Suconic</a>.
* */
public class MultiThreadWriteNativeTest extends AIOTestBase
{
static Logger log = Logger.getLogger(MultiThreadWriteNativeTest.class);
AtomicInteger position = new AtomicInteger(0);
static final int SIZE = 1024;
static final int NUMBER_OF_THREADS = 10;
static final int NUMBER_OF_LINES = 1000;
// Executor exec
Executor executor = Executors.newSingleThreadExecutor();
@Override
protected void setUp() throws Exception
{
super.setUp();
position.set(0);
}
@Override
protected void tearDown() throws Exception
{
super.tearDown();
}
public void testMultipleASynchronousWrites() throws Throwable
{
executeTest(false);
}
public void testMultipleSynchronousWrites() throws Throwable
{
executeTest(true);
}
private void executeTest(final boolean sync) throws Throwable
{
log.debug(sync ? "Sync test:" : "Async test");
AsynchronousFileImpl jlibAIO = new AsynchronousFileImpl();
jlibAIO.open(FILE_NAME, 21000);
try
{
log.debug("Preallocating file");
jlibAIO.fill(0l, NUMBER_OF_THREADS, SIZE * NUMBER_OF_LINES, (byte)0);
log.debug("Done Preallocating file");
CountDownLatch latchStart = new CountDownLatch(NUMBER_OF_THREADS + 1);
ArrayList<ThreadProducer> list = new ArrayList<ThreadProducer>(NUMBER_OF_THREADS);
for (int i = 0; i < NUMBER_OF_THREADS; i++)
{
ThreadProducer producer = new ThreadProducer("Thread " + i, latchStart, jlibAIO, sync);
list.add(producer);
producer.start();
}
latchStart.countDown();
latchStart.await();
long startTime = System.currentTimeMillis();
for (ThreadProducer producer : list)
{
producer.join();
if (producer.failed != null)
{
throw producer.failed;
}
}
long endTime = System.currentTimeMillis();
log.debug((sync ? "Sync result:" : "Async result:") + " Records/Second = " +
NUMBER_OF_THREADS *
NUMBER_OF_LINES *
1000 /
(endTime - startTime) +
" total time = " +
(endTime - startTime) +
" total number of records = " +
NUMBER_OF_THREADS *
NUMBER_OF_LINES);
}
finally
{
jlibAIO.close();
}
}
private int getNewPosition()
{
return position.addAndGet(1);
}
class ThreadProducer extends Thread
{
Throwable failed = null;
CountDownLatch latchStart;
boolean sync;
AsynchronousFileImpl libaio;
public ThreadProducer(final String name,
final CountDownLatch latchStart,
final AsynchronousFileImpl libaio,
final boolean sync)
{
super(name);
this.latchStart = latchStart;
this.libaio = libaio;
this.sync = sync;
}
@Override
public void run()
{
super.run();
try
{
ByteBuffer buffer = libaio.newBuffer(SIZE);
// I'm aways reusing the same buffer, as I don't want any noise from
// malloc on the measurement
// Encoding buffer
addString("Thread name=" + Thread.currentThread().getName() + ";" + "\n", buffer);
for (int local = buffer.position(); local < buffer.capacity() - 1; local++)
{
buffer.put((byte)' ');
}
buffer.put((byte)'\n');
latchStart.countDown();
latchStart.await();
long startTime = System.currentTimeMillis();
CountDownLatch latchFinishThread = null;
if (!sync)
{
latchFinishThread = new CountDownLatch(NUMBER_OF_LINES);
}
LinkedList<CountDownCallback> list = new LinkedList<CountDownCallback>();
for (int i = 0; i < NUMBER_OF_LINES; i++)
{
if (sync)
{
latchFinishThread = new CountDownLatch(1);
}
CountDownCallback callback = new CountDownCallback(latchFinishThread);
if (!sync)
{
list.add(callback);
}
addData(libaio, buffer, callback);
if (sync)
{
latchFinishThread.await();
assertTrue(callback.doneCalled);
assertFalse(callback.errorCalled);
}
}
if (!sync)
{
latchFinishThread.await();
}
for (CountDownCallback callback : list)
{
assertTrue(callback.doneCalled);
assertFalse(callback.errorCalled);
}
long endtime = System.currentTimeMillis();
log.debug(Thread.currentThread().getName() + " Rec/Sec= " +
NUMBER_OF_LINES *
1000 /
(endtime - startTime) +
" total time = " +
(endtime - startTime) +
" number of lines=" +
NUMBER_OF_LINES);
for (CountDownCallback callback : list)
{
assertTrue(callback.doneCalled);
assertFalse(callback.errorCalled);
}
}
catch (Throwable e)
{
e.printStackTrace();
failed = e;
}
}
}
private static void addString(final String str, final ByteBuffer buffer)
{
byte bytes[] = str.getBytes();
buffer.put(bytes);
}
private void addData(final AsynchronousFileImpl aio, final ByteBuffer buffer, final AIOCallback callback) throws Exception
{
executor.execute(new WriteRunnable(aio, buffer, callback));
}
private class WriteRunnable implements Runnable
{
AsynchronousFileImpl aio;
ByteBuffer buffer;
AIOCallback callback;
public WriteRunnable(final AsynchronousFileImpl aio, final ByteBuffer buffer, final AIOCallback callback)
{
this.aio = aio;
this.buffer = buffer;
this.callback = callback;
}
public void run()
{
try
{
aio.write(getNewPosition() * SIZE, SIZE, buffer, callback);
}
catch (Exception e)
{
callback.onError(-1, e.toString());
e.printStackTrace();
}
}
}
}