ARTEMIS-163 First pass on the native AIO refactoring

https://issues.apache.org/jira/browse/ARTEMIS-163

On this pass I'm just converting the native layer to a simpler one.
It wasn't very easy to change the alignment at the current framework,
so I did some refactoring simplifying the native layer

The volume of the nubmer of changes here is because:

- The API is changed, we now don't close the libaio queue between files
- The native layer won't use malloc as much as it used to, saving some CPU and memory defragmentation
- I organized the code around nio and libaio
This commit is contained in:
Clebert Suconic 2015-07-27 16:15:03 -04:00
parent 661f695ee2
commit 6fe9e0ebd6
166 changed files with 3913 additions and 4618 deletions

2
.gitignore vendored
View File

@ -17,4 +17,4 @@ ratReport.txt
**/cmake_install.cmake **/cmake_install.cmake
# this file is generated # this file is generated
artemis-native/src/main/c/org_apache_activemq_artemis_core_libaio_Native.h artemis-native/src/main/c/org_apache_activemq_artemis_jlibaio_LibaioContext.h

18
CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (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.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
SUBDIRS(artemis-native)

View File

@ -4,7 +4,7 @@ This file describes some minimum 'stuff one needs to know' to get started coding
## Source ## Source
For details about the modifying the code, building the project, running tests, IDE integration, etc. see For details about the modifying the code, building the project, running tests, IDE integration, etc. see
our [Hacking Guide](./docs/hacking-guide/en/SUMMARY.md). our [Hacking Guide](./docs/hacking-guide/en/SUMMARY.md).
## Examples ## Examples

View File

@ -26,6 +26,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermission;
import java.text.DecimalFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -36,8 +37,10 @@ import java.util.regex.Pattern;
import io.airlift.airline.Arguments; import io.airlift.airline.Arguments;
import io.airlift.airline.Command; import io.airlift.airline.Command;
import io.airlift.airline.Option; import io.airlift.airline.Option;
import org.apache.activemq.artemis.core.asyncio.impl.AsynchronousFileImpl; import org.apache.activemq.artemis.cli.commands.util.SyncCalculation;
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
import org.apache.activemq.artemis.jlibaio.LibaioContext;
import org.apache.activemq.artemis.jlibaio.LibaioFile;
import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE; import static java.nio.file.attribute.PosixFilePermission.GROUP_EXECUTE;
import static java.nio.file.attribute.PosixFilePermission.GROUP_READ; import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
@ -84,6 +87,7 @@ public class Create extends InputAbstract
public static final String ETC_CLUSTER_SETTINGS_TXT = "etc/cluster-settings.txt"; public static final String ETC_CLUSTER_SETTINGS_TXT = "etc/cluster-settings.txt";
public static final String ETC_CONNECTOR_SETTINGS_TXT = "etc/connector-settings.txt"; public static final String ETC_CONNECTOR_SETTINGS_TXT = "etc/connector-settings.txt";
public static final String ETC_BOOTSTRAP_WEB_SETTINGS_TXT = "etc/bootstrap-web-settings.txt"; public static final String ETC_BOOTSTRAP_WEB_SETTINGS_TXT = "etc/bootstrap-web-settings.txt";
public static final String ETC_JOURNAL_BUFFER_SETTINGS = "etc/journal-buffer-settings.txt";
@Arguments(description = "The instance directory to hold the broker's configuration and data. Path must be writable.", required = true) @Arguments(description = "The instance directory to hold the broker's configuration and data. Path must be writable.", required = true)
File directory; File directory;
@ -142,6 +146,9 @@ public class Create extends InputAbstract
@Option(name = "--require-login", description = "This will configure security to require user / password, opposite of --allow-anonymous") @Option(name = "--require-login", description = "This will configure security to require user / password, opposite of --allow-anonymous")
Boolean requireLogin = null; Boolean requireLogin = null;
@Option(name = "--no-sync-test", description = "Disable the calculation for the buffer")
boolean noSyncTest;
@Option(name = "--user", description = "The username (Default: input)") @Option(name = "--user", description = "The username (Default: input)")
String user; String user;
@ -529,12 +536,16 @@ public class Create extends InputAbstract
filters.put("${shared-store.settings}", ""); filters.put("${shared-store.settings}", "");
} }
if (IS_WINDOWS || !AsynchronousFileImpl.isLoaded()) boolean aio;
if (IS_WINDOWS || !supportsLibaio())
{ {
aio = false;
filters.put("${journal.settings}", "NIO"); filters.put("${journal.settings}", "NIO");
} }
else else
{ {
aio = true;
filters.put("${journal.settings}", "ASYNCIO"); filters.put("${journal.settings}", "ASYNCIO");
} }
@ -590,7 +601,8 @@ public class Create extends InputAbstract
new File(directory, "etc").mkdirs(); new File(directory, "etc").mkdirs();
new File(directory, "log").mkdirs(); new File(directory, "log").mkdirs();
new File(directory, "tmp").mkdirs(); new File(directory, "tmp").mkdirs();
new File(directory, "data").mkdirs(); File dataFolder = new File(directory, "data");
dataFolder.mkdirs();
if (javaOptions == null || javaOptions.length() == 0) if (javaOptions == null || javaOptions.length() == 0)
{ {
@ -638,7 +650,7 @@ public class Create extends InputAbstract
filters.put("${bootstrap-web-settings}", applyFilters(readTextFile(ETC_BOOTSTRAP_WEB_SETTINGS_TXT), filters)); filters.put("${bootstrap-web-settings}", applyFilters(readTextFile(ETC_BOOTSTRAP_WEB_SETTINGS_TXT), filters));
} }
performSyncCalc(filters, aio, dataFolder);
write(ETC_BOOTSTRAP_XML, filters, false); write(ETC_BOOTSTRAP_XML, filters, false);
write(ETC_BROKER_XML, filters, false); write(ETC_BROKER_XML, filters, false);
@ -694,6 +706,76 @@ public class Create extends InputAbstract
return null; return null;
} }
private void performSyncCalc(HashMap<String, String> filters, boolean aio, File dataFolder)
{
if (noSyncTest)
{
filters.put("${journal-buffer.settings}", "");
}
else
{
try
{
int writes = 250;
System.out.println("");
System.out.println("Performing write sync calculation...");
long time = SyncCalculation.syncTest(dataFolder, 4096, writes, 5, verbose, aio);
long nanoseconds = SyncCalculation.toNanos(time, writes);
double writesPerMillisecond = (double)writes / (double) time;
String writesPerMillisecondStr = new DecimalFormat("###.##").format(writesPerMillisecond);
HashMap<String, String> syncFilter = new HashMap<String, String>();
syncFilter.put("${nanoseconds}", Long.toString(nanoseconds));
syncFilter.put("${writesPerMillisecond}", writesPerMillisecondStr);
System.out.println("done! Your system can make " + writesPerMillisecondStr +
" writes per millisecond, your journal-buffer-timeout will be " + nanoseconds);
filters.put("${journal-buffer.settings}", applyFilters(readTextFile(ETC_JOURNAL_BUFFER_SETTINGS), syncFilter));
}
catch (Exception e)
{
filters.put("${journal-buffer.settings}", "");
e.printStackTrace();
System.err.println("Couldn't perform sync calculation, using default values");
}
}
}
private boolean supportsLibaio()
{
if (LibaioContext.isLoaded())
{
try (LibaioContext context = new LibaioContext(1, true))
{
File tmpFile = new File(directory, "validateAIO.bin");
boolean supportsLibaio = true;
try
{
LibaioFile file = context.openFile(tmpFile, true);
file.close();
}
catch (Exception e)
{
supportsLibaio = false;
}
tmpFile.delete();
if (!supportsLibaio)
{
System.err.println("The filesystem used on " + directory + " doesn't support libAIO and O_DIRECT files, switching journal-type to NIO");
}
return supportsLibaio;
}
}
else
{
return false;
}
}
private void makeExec(String path) throws IOException private void makeExec(String path) throws IOException
{ {
try try

View File

@ -22,9 +22,9 @@ import io.airlift.airline.Command;
import org.apache.activemq.artemis.cli.commands.Action; import org.apache.activemq.artemis.cli.commands.Action;
import org.apache.activemq.artemis.cli.commands.ActionContext; import org.apache.activemq.artemis.cli.commands.ActionContext;
import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.journal.IOCriticalErrorListener; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener;
import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
@Command(name = "compact", description = "Compacts the journal of a non running server") @Command(name = "compact", description = "Compacts the journal of a non running server")
public final class CompactJournal extends DataAbstract implements Action public final class CompactJournal extends DataAbstract implements Action
@ -54,7 +54,7 @@ public final class CompactJournal extends DataAbstract implements Action
final int fileSize, final int fileSize,
final IOCriticalErrorListener listener) throws Exception final IOCriticalErrorListener listener) throws Exception
{ {
NIOSequentialFileFactory nio = new NIOSequentialFileFactory(directory, listener); NIOSequentialFileFactory nio = new NIOSequentialFileFactory(directory, listener, 1);
JournalImpl journal = new JournalImpl(fileSize, minFiles, 0, 0, nio, journalPrefix, journalSuffix, 1); JournalImpl journal = new JournalImpl(fileSize, minFiles, 0, 0, nio, journalPrefix, journalSuffix, 1);

View File

@ -35,7 +35,7 @@ import org.apache.activemq.artemis.cli.commands.Configurable;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
import org.apache.activemq.artemis.core.journal.impl.JournalRecord; import org.apache.activemq.artemis.core.journal.impl.JournalRecord;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.utils.Base64; import org.apache.activemq.artemis.utils.Base64;
@Command(name = "decode", description = "Decode a journal's internal format into a new journal set of files") @Command(name = "decode", description = "Decode a journal's internal format into a new journal set of files")
@ -117,7 +117,7 @@ public class DecodeJournal extends Configurable implements Action
System.err.println("Could not create directory " + directory); System.err.println("Could not create directory " + directory);
} }
NIOSequentialFileFactory nio = new NIOSequentialFileFactory(new File(directory), null); NIOSequentialFileFactory nio = new NIOSequentialFileFactory(new File(directory), null, 1);
JournalImpl journal = new JournalImpl(fileSize, minFiles, 0, 0, nio, journalPrefix, journalSuffix, 1); JournalImpl journal = new JournalImpl(fileSize, minFiles, 0, 0, nio, journalPrefix, journalSuffix, 1);

View File

@ -28,11 +28,11 @@ import org.apache.activemq.artemis.cli.commands.Action;
import org.apache.activemq.artemis.cli.commands.ActionContext; import org.apache.activemq.artemis.cli.commands.ActionContext;
import org.apache.activemq.artemis.cli.commands.Configurable; import org.apache.activemq.artemis.cli.commands.Configurable;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.JournalFile; import org.apache.activemq.artemis.core.journal.impl.JournalFile;
import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
import org.apache.activemq.artemis.core.journal.impl.JournalReaderCallback; import org.apache.activemq.artemis.core.journal.impl.JournalReaderCallback;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.utils.Base64; import org.apache.activemq.artemis.utils.Base64;
@Command(name = "encode", description = "Encode a set of journal files into an internal encoded data format") @Command(name = "encode", description = "Encode a set of journal files into an internal encoded data format")
@ -113,7 +113,7 @@ public class EncodeJournal extends Configurable implements Action
final int fileSize, final int fileSize,
final PrintStream out) throws Exception final PrintStream out) throws Exception
{ {
NIOSequentialFileFactory nio = new NIOSequentialFileFactory(new File(directory), null); NIOSequentialFileFactory nio = new NIOSequentialFileFactory(new File(directory), null, 1);
JournalImpl journal = new JournalImpl(fileSize, minFiles, 0, 0, nio, journalPrefix, journalSuffix, 1); JournalImpl journal = new JournalImpl(fileSize, minFiles, 0, 0, nio, journalPrefix, journalSuffix, 1);

View File

@ -52,10 +52,10 @@ import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.Journal;
import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.TransactionFailureCallback; import org.apache.activemq.artemis.core.journal.TransactionFailureCallback;
import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.core.message.BodyEncoder; import org.apache.activemq.artemis.core.message.BodyEncoder;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
import org.apache.activemq.artemis.core.paging.PagingManager; import org.apache.activemq.artemis.core.paging.PagingManager;
@ -345,7 +345,7 @@ public final class XmlDataExporter extends DataAbstract implements Action
private void getJmsBindings() throws Exception private void getJmsBindings() throws Exception
{ {
SequentialFileFactory bindingsJMS = new NIOSequentialFileFactory(config.getBindingsLocation()); SequentialFileFactory bindingsJMS = new NIOSequentialFileFactory(config.getBindingsLocation(), 1);
Journal jmsJournal = new JournalImpl(1024 * 1024, Journal jmsJournal = new JournalImpl(1024 * 1024,
2, 2,

View File

@ -0,0 +1,190 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.cli.commands.util;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.io.aio.AIOSequentialFileFactory;
import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.jlibaio.LibaioContext;
import org.apache.activemq.artemis.utils.ReusableLatch;
/**
* It will perform a simple test to evaluate how many syncs a disk can make per second
* * *
*/
public class SyncCalculation
{
/**
* It will perform a write test of blockSize * bocks, sinc on each write, for N tries.
* It will return the lowest spent time from the tries.
*/
public static long syncTest(File datafolder, int blockSize, int blocks, int tries, boolean verbose, boolean aio) throws Exception
{
SequentialFileFactory factory = newFactory(datafolder, aio);
SequentialFile file = factory.createSequentialFile("test.tmp");
try
{
file.delete();
file.open();
file.fill(blockSize * blocks);
long[] result = new long[tries];
byte[] block = new byte[blockSize];
for (int i = 0; i < block.length; i++)
{
block[i] = (byte) 't';
}
ByteBuffer bufferBlock = factory.newBuffer(blockSize);
bufferBlock.put(block);
bufferBlock.position(0);
final ReusableLatch latch = new ReusableLatch(0);
IOCallback callback = new IOCallback()
{
@Override
public void done()
{
latch.countDown();
}
@Override
public void onError(int errorCode, String errorMessage)
{
}
};
DecimalFormat dcformat = new DecimalFormat("###.##");
for (int ntry = 0; ntry < tries; ntry++)
{
if (verbose)
{
System.out.println("**************************************************");
System.out.println(ntry + " of " + tries + " calculation");
}
file.position(0);
long start = System.currentTimeMillis();
for (int i = 0; i < blocks; i++)
{
bufferBlock.position(0);
latch.countUp();
file.writeDirect(bufferBlock, true, callback);
if (!latch.await(5, TimeUnit.SECONDS))
{
throw new IOException("Callback wasn't called");
}
}
long end = System.currentTimeMillis();
result[ntry] = (end - start);
if (verbose)
{
double writesPerMillisecond = (double)blocks / (double) result[ntry];
System.out.println("Time = " + result[ntry]);
System.out.println("Writes / millisecond = " + dcformat.format(writesPerMillisecond));
System.out.println("bufferTimeout = " + toNanos(result[ntry], blocks));
System.out.println("**************************************************");
}
}
factory.releaseDirectBuffer(bufferBlock);
long totalTime = Long.MAX_VALUE;
for (int i = 0; i < tries; i++)
{
if (result[i] < totalTime)
{
totalTime = result[i];
}
}
return totalTime;
}
finally
{
try
{
file.close();
}
catch (Exception e)
{
}
try
{
file.delete();
}
catch (Exception e)
{
}
try
{
factory.stop();
}
catch (Exception e)
{
}
}
}
public static long toNanos(long time, long blocks)
{
double blocksPerMillisecond = (double) blocks / (double) (time);
long nanoSeconds = TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS);
long timeWait = (long) (nanoSeconds / blocksPerMillisecond);
return timeWait;
}
private static SequentialFileFactory newFactory(File datafolder, boolean aio)
{
if (aio && LibaioContext.isLoaded())
{
SequentialFileFactory factory = new AIOSequentialFileFactory(datafolder, 1);
factory.start();
((AIOSequentialFileFactory) factory).disableBufferReuse();
return factory;
}
else
{
SequentialFileFactory factory = new NIOSequentialFileFactory(datafolder, 1);
factory.start();
return factory;
}
}
}

View File

@ -42,6 +42,7 @@ under the License.
<large-messages-directory>${data.dir}/large-messages</large-messages-directory> <large-messages-directory>${data.dir}/large-messages</large-messages-directory>
<journal-min-files>10</journal-min-files> <journal-min-files>10</journal-min-files>
${journal-buffer.settings}
${connector-config.settings} ${connector-config.settings}
<acceptors> <acceptors>
<!-- Default ActiveMQ Artemis Acceptor. Multi-protocol adapter. Currently supports Core, OpenWire, Stomp and AMQP. --> <!-- Default ActiveMQ Artemis Acceptor. Multi-protocol adapter. Currently supports Core, OpenWire, Stomp and AMQP. -->

View File

@ -0,0 +1,8 @@
<!--
This value was determined through a calculation.
Your system could perform ${writesPerMillisecond} writes per millisecond
on the current journal configuration.
That translates as a sync write every ${nanoseconds} nanoseconds
-->
<journal-buffer-timeout>${nanoseconds}</journal-buffer-timeout>

View File

@ -25,6 +25,8 @@ import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.cli.Artemis; import org.apache.activemq.artemis.cli.Artemis;
import org.apache.activemq.artemis.cli.commands.Run; import org.apache.activemq.artemis.cli.commands.Run;
import org.apache.activemq.artemis.cli.commands.util.SyncCalculation;
import org.apache.activemq.artemis.jlibaio.LibaioContext;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.apache.activemq.artemis.jms.client.ActiveMQDestination; import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
import org.junit.After; import org.junit.After;
@ -68,6 +70,20 @@ public class ArtemisTest
testCli("create","/rawr"); testCli("create","/rawr");
} }
@Test
public void testSync() throws Exception
{
int writes = 2560;
int tries = 10;
long totalAvg = SyncCalculation.syncTest(temporaryFolder.getRoot(), 4096, writes, tries, true, true);
System.out.println();
System.out.println("TotalAvg = " + totalAvg);
long nanoTime = SyncCalculation.toNanos(totalAvg, writes);
System.out.println("nanoTime avg = " + nanoTime);
Assert.assertEquals(0, LibaioContext.getTotalMaxIO());
}
@Test @Test
public void testSimpleRun() throws Exception public void testSimpleRun() throws Exception
{ {
@ -75,9 +91,9 @@ public class ArtemisTest
Artemis.main("create", temporaryFolder.getRoot().getAbsolutePath(), "--force", "--silent-input", "--no-web"); Artemis.main("create", temporaryFolder.getRoot().getAbsolutePath(), "--force", "--silent-input", "--no-web");
System.setProperty("artemis.instance", temporaryFolder.getRoot().getAbsolutePath()); System.setProperty("artemis.instance", temporaryFolder.getRoot().getAbsolutePath());
// Some exceptions may happen on the initialization, but they should be ok on start the basic core protocol // Some exceptions may happen on the initialization, but they should be ok on start the basic core protocol
Artemis.main("run"); Artemis.execute("run");
Assert.assertEquals(Integer.valueOf(70), Artemis.execute("producer", "--txt-size", "50", "--message-count", "70", "--verbose")); Assert.assertEquals(Integer.valueOf(1000), Artemis.execute("producer", "--message-count", "1000", "--verbose"));
Assert.assertEquals(Integer.valueOf(70), Artemis.execute("consumer", "--txt-size", "50", "--verbose", "--break-on-null", "--receive-timeout", "100")); Assert.assertEquals(Integer.valueOf(1000), Artemis.execute("consumer", "--verbose", "--break-on-null", "--receive-timeout", "100"));
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61616"); ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = cf.createConnection(); Connection connection = cf.createConnection();
@ -116,6 +132,7 @@ public class ArtemisTest
Artemis.execute("stop"); Artemis.execute("stop");
Assert.assertTrue(Run.latchRunning.await(5, TimeUnit.SECONDS)); Assert.assertTrue(Run.latchRunning.await(5, TimeUnit.SECONDS));
Assert.assertEquals(0, LibaioContext.getTotalMaxIO());
} }

View File

@ -49,6 +49,7 @@ public class StreamClassPathTest
openStream(Create.ETC_CLUSTER_SETTINGS_TXT); openStream(Create.ETC_CLUSTER_SETTINGS_TXT);
openStream(Create.ETC_CONNECTOR_SETTINGS_TXT); openStream(Create.ETC_CONNECTOR_SETTINGS_TXT);
openStream(Create.ETC_BOOTSTRAP_WEB_SETTINGS_TXT); openStream(Create.ETC_BOOTSTRAP_WEB_SETTINGS_TXT);
openStream(Create.ETC_JOURNAL_BUFFER_SETTINGS);
} }

View File

@ -34,4 +34,9 @@ public final class ActiveMQNativeIOError extends ActiveMQException
{ {
super(ActiveMQExceptionType.NATIVE_ERROR_CANT_INITIALIZE_AIO, msg); super(ActiveMQExceptionType.NATIVE_ERROR_CANT_INITIALIZE_AIO, msg);
} }
public ActiveMQNativeIOError(String msg, Throwable e)
{
super(ActiveMQExceptionType.NATIVE_ERROR_CANT_INITIALIZE_AIO, msg, e);
}
} }

View File

@ -29,9 +29,9 @@ import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.Journal;
import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.core.replication.ReplicatedJournal; import org.apache.activemq.artemis.core.replication.ReplicatedJournal;
import org.apache.activemq.artemis.core.replication.ReplicationManager; import org.apache.activemq.artemis.core.replication.ReplicationManager;
import org.apache.activemq.artemis.core.server.JournalType; import org.apache.activemq.artemis.core.server.JournalType;
@ -87,7 +87,7 @@ public final class JMSJournalStorageManagerImpl implements JMSStorageManager
createDir = config.isCreateBindingsDir(); createDir = config.isCreateBindingsDir();
SequentialFileFactory bindingsJMS = new NIOSequentialFileFactory(config.getBindingsLocation()); SequentialFileFactory bindingsJMS = new NIOSequentialFileFactory(config.getBindingsLocation(), 1);
Journal localJMS = new JournalImpl(1024 * 1024, Journal localJMS = new JournalImpl(1024 * 1024,
2, 2,

View File

@ -1,58 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.asyncio;
import java.nio.ByteBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException;
public interface AsynchronousFile
{
void close() throws InterruptedException, ActiveMQException;
/**
*
* Note: If you are using a native Linux implementation, maxIO can't be higher than what's defined on /proc/sys/fs/aio-max-nr, or you would get an error
* @param fileName
* @param maxIO The number of max concurrent asynchronous IO operations. It has to be balanced between the size of your writes and the capacity of your disk.
* @throws ActiveMQException
*/
void open(String fileName, int maxIO) throws ActiveMQException;
/**
* Warning: This function will perform a synchronous IO, probably translating to a fstat call
* @throws ActiveMQException
* */
long size() throws ActiveMQException;
/** Any error will be reported on the callback interface */
void write(long position, long size, ByteBuffer directByteBuffer, AIOCallback aioCallback);
/**
* Performs an internal direct write.
* @throws ActiveMQException
*/
void writeInternal(long positionToWrite, long size, ByteBuffer bytes) throws ActiveMQException;
void read(long position, long size, ByteBuffer directByteBuffer, AIOCallback aioCallback) throws ActiveMQException;
void fill(long position, int blocks, long size, byte fillChar) throws ActiveMQException;
void setBufferCallback(BufferCallback callback);
int getBlockSize();
}

View File

@ -1,822 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.asyncio.impl;
import java.nio.ByteBuffer;
import java.nio.channels.FileLock;
import java.util.PriorityQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
import org.apache.activemq.artemis.core.asyncio.AIOCallback;
import org.apache.activemq.artemis.core.asyncio.AsynchronousFile;
import org.apache.activemq.artemis.core.asyncio.BufferCallback;
import org.apache.activemq.artemis.core.asyncio.IOExceptionListener;
import org.apache.activemq.artemis.core.libaio.Native;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
import org.apache.activemq.artemis.utils.ReusableLatch;
/**
* AsynchronousFile implementation
*
* Warning: Case you refactor the name or the package of this class
* You need to make sure you also rename the C++ native calls
*/
public class AsynchronousFileImpl implements AsynchronousFile
{
// Static ----------------------------------------------------------------------------
private static final AtomicInteger totalMaxIO = new AtomicInteger(0);
private static boolean loaded = false;
/**
* This definition needs to match Version.h on the native sources.
* <br>
* Or else the native module won't be loaded because of version mismatches
*/
private static final int EXPECTED_NATIVE_VERSION = 52;
/**
* Used to determine the next writing sequence
*/
private final AtomicLong nextWritingSequence = new AtomicLong(0);
/**
* Used to determine the next writing sequence.
* This is accessed from a single thread (the Poller Thread)
*/
private long nextReadSequence = 0;
/**
* AIO can't guarantee ordering over callbacks.
* <br>
* We use this {@link PriorityQueue} to hold values until they are in order
*/
private final PriorityQueue<CallbackHolder> pendingCallbacks = new PriorityQueue<CallbackHolder>();
public static void addMax(final int io)
{
AsynchronousFileImpl.totalMaxIO.addAndGet(io);
}
/**
* For test purposes
*/
public static int getTotalMaxIO()
{
return AsynchronousFileImpl.totalMaxIO.get();
}
public static void resetMaxAIO()
{
AsynchronousFileImpl.totalMaxIO.set(0);
}
public static int openFile(String fileName)
{
return Native.openFile(fileName);
}
public static void closeFile(int handle)
{
Native.closeFile(handle);
}
public static void destroyBuffer(ByteBuffer buffer)
{
Native.destroyBuffer(buffer);
}
private static boolean loadLibrary(final String name)
{
try
{
ActiveMQJournalLogger.LOGGER.trace(name + " being loaded");
System.loadLibrary(name);
if (Native.getNativeVersion() != AsynchronousFileImpl.EXPECTED_NATIVE_VERSION)
{
ActiveMQJournalLogger.LOGGER.incompatibleNativeLibrary();
return false;
}
else
{
return true;
}
}
catch (Throwable e)
{
ActiveMQJournalLogger.LOGGER.debug(name + " -> error loading the native library", e);
return false;
}
}
static
{
String[] libraries = new String[]{"artemis-native", "artemis-native-64", "artemis-native-32"};
for (String library : libraries)
{
if (AsynchronousFileImpl.loadLibrary(library))
{
AsynchronousFileImpl.loaded = true;
break;
}
else
{
ActiveMQJournalLogger.LOGGER.debug("Library " + library + " not found!");
}
}
if (!AsynchronousFileImpl.loaded)
{
ActiveMQJournalLogger.LOGGER.debug("Couldn't locate LibAIO Wrapper");
}
}
public static boolean isLoaded()
{
return AsynchronousFileImpl.loaded;
}
// Attributes ------------------------------------------------------------------------
private boolean opened = false;
private String fileName;
/**
* Used while inside the callbackDone and callbackError
*/
private final Lock callbackLock = new ReentrantLock();
private final ReusableLatch pollerLatch = new ReusableLatch();
private volatile Runnable poller;
private int maxIO;
private final Lock writeLock = new ReentrantReadWriteLock().writeLock();
private final ReusableLatch pendingWrites = new ReusableLatch();
private Semaphore maxIOSemaphore;
private BufferCallback bufferCallback;
/**
* A callback for IO errors when they happen
*/
private final IOExceptionListener ioExceptionListener;
/**
* Warning: Beware of the C++ pointer! It will bite you! :-)
*/
private ByteBuffer handler;
// A context switch on AIO would make it to synchronize the disk before
// switching to the new thread, what would cause
// serious performance problems. Because of that we make all the writes on
// AIO using a single thread.
private final Executor writeExecutor;
private final Executor pollerExecutor;
// AsynchronousFile implementation ---------------------------------------------------
/**
* @param writeExecutor It needs to be a single Thread executor. If null it will use the user thread to execute write operations
* @param pollerExecutor The thread pool that will initialize poller handlers
*/
public AsynchronousFileImpl(final Executor writeExecutor, final Executor pollerExecutor, final IOExceptionListener ioExceptionListener)
{
this.writeExecutor = writeExecutor;
this.pollerExecutor = pollerExecutor;
this.ioExceptionListener = ioExceptionListener;
}
public AsynchronousFileImpl(final Executor writeExecutor, final Executor pollerExecutor)
{
this(writeExecutor, pollerExecutor, null);
}
public void open(final String fileName1, final int maxIOArgument) throws ActiveMQException
{
writeLock.lock();
try
{
if (opened)
{
throw new IllegalStateException("AsynchronousFile is already opened");
}
this.maxIO = maxIOArgument;
maxIOSemaphore = new Semaphore(this.maxIO);
this.fileName = fileName1;
try
{
handler = Native.init(AsynchronousFileImpl.class, fileName1, this.maxIO, ActiveMQJournalLogger.LOGGER);
}
catch (ActiveMQException e)
{
ActiveMQException ex = null;
if (e.getType() == ActiveMQExceptionType.NATIVE_ERROR_CANT_INITIALIZE_AIO)
{
ex = new ActiveMQException(e.getType(),
"Can't initialize AIO. Currently AIO in use = " + AsynchronousFileImpl.totalMaxIO.get() +
", trying to allocate more " +
maxIOArgument,
e);
}
else
{
ex = e;
}
throw ex;
}
opened = true;
AsynchronousFileImpl.addMax(this.maxIO);
nextWritingSequence.set(0);
nextReadSequence = 0;
}
finally
{
writeLock.unlock();
}
}
public void close() throws InterruptedException, ActiveMQException
{
checkOpened();
writeLock.lock();
try
{
while (!pendingWrites.await(60000))
{
ActiveMQJournalLogger.LOGGER.couldNotGetLock(fileName);
}
while (!maxIOSemaphore.tryAcquire(maxIO, 60, TimeUnit.SECONDS))
{
ActiveMQJournalLogger.LOGGER.couldNotGetLock(fileName);
}
maxIOSemaphore = null;
if (poller != null)
{
stopPoller();
}
if (handler != null)
{
Native.closeInternal(handler);
AsynchronousFileImpl.addMax(-maxIO);
}
opened = false;
handler = null;
}
finally
{
writeLock.unlock();
}
}
public void writeInternal(long positionToWrite, long size, ByteBuffer bytes) throws ActiveMQException
{
try
{
Native.writeInternal(handler, positionToWrite, size, bytes);
}
catch (ActiveMQException e)
{
fireExceptionListener(e.getType().getCode(), e.getMessage());
throw e;
}
if (bufferCallback != null)
{
bufferCallback.bufferDone(bytes);
}
}
public void write(final long position,
final long size,
final ByteBuffer directByteBuffer,
final AIOCallback aioCallback)
{
if (aioCallback == null)
{
throw new NullPointerException("Null Callback");
}
checkOpened();
if (poller == null)
{
startPoller();
}
pendingWrites.countUp();
if (writeExecutor != null)
{
maxIOSemaphore.acquireUninterruptibly();
writeExecutor.execute(new Runnable()
{
public void run()
{
long sequence = nextWritingSequence.getAndIncrement();
try
{
Native.write(AsynchronousFileImpl.this, handler, sequence, position, size, directByteBuffer, aioCallback);
}
catch (ActiveMQException e)
{
callbackError(aioCallback, sequence, directByteBuffer, e.getType().getCode(), e.getMessage());
}
catch (RuntimeException e)
{
callbackError(aioCallback,
sequence,
directByteBuffer,
ActiveMQExceptionType.INTERNAL_ERROR.getCode(),
e.getMessage());
}
}
});
}
else
{
maxIOSemaphore.acquireUninterruptibly();
long sequence = nextWritingSequence.getAndIncrement();
try
{
Native.write(this, handler, sequence, position, size, directByteBuffer, aioCallback);
}
catch (ActiveMQException e)
{
callbackError(aioCallback, sequence, directByteBuffer, e.getType().getCode(), e.getMessage());
}
catch (RuntimeException e)
{
callbackError(aioCallback, sequence, directByteBuffer, ActiveMQExceptionType.INTERNAL_ERROR.getCode(), e.getMessage());
}
}
}
public void read(final long position,
final long size,
final ByteBuffer directByteBuffer,
final AIOCallback aioPackage) throws ActiveMQException
{
checkOpened();
if (poller == null)
{
startPoller();
}
pendingWrites.countUp();
maxIOSemaphore.acquireUninterruptibly();
try
{
Native.read(this, handler, position, size, directByteBuffer, aioPackage);
}
catch (ActiveMQException e)
{
// Release only if an exception happened
maxIOSemaphore.release();
pendingWrites.countDown();
throw e;
}
catch (RuntimeException e)
{
// Release only if an exception happened
maxIOSemaphore.release();
pendingWrites.countDown();
throw e;
}
}
public long size() throws ActiveMQException
{
checkOpened();
return Native.size0(handler);
}
public void fill(final long position, final int blocks, final long size, final byte fillChar) throws ActiveMQException
{
checkOpened();
try
{
Native.fill(handler, position, blocks, size, fillChar);
}
catch (ActiveMQException e)
{
fireExceptionListener(e.getType().getCode(), e.getMessage());
throw e;
}
}
public int getBlockSize()
{
return 512;
}
/**
* This needs to be synchronized because of
* http://bugs.sun.com/view_bug.do?bug_id=6791815
* http://mail.openjdk.java.net/pipermail/hotspot-runtime-dev/2009-January/000386.html
*/
public static synchronized ByteBuffer newBuffer(final int size)
{
if (size % 512 != 0)
{
throw new RuntimeException("Buffer size needs to be aligned to 512");
}
return Native.newNativeBuffer(size);
}
public void setBufferCallback(final BufferCallback callback)
{
bufferCallback = callback;
}
/**
* Return the JNI handler used on C++
*/
public ByteBuffer getHandler()
{
return handler;
}
public static void clearBuffer(final ByteBuffer buffer)
{
Native.resetBuffer(buffer, buffer.limit());
buffer.position(0);
}
// Protected -------------------------------------------------------------------------
@Override
protected void finalize()
{
if (opened)
{
ActiveMQJournalLogger.LOGGER.fileFinalizedWhileOpen(fileName);
}
}
// Private ---------------------------------------------------------------------------
private void callbackDone(final AIOCallback callback, final long sequence, final ByteBuffer buffer)
{
maxIOSemaphore.release();
pendingWrites.countDown();
callbackLock.lock();
try
{
if (sequence == -1)
{
callback.done();
}
else
{
if (sequence == nextReadSequence)
{
nextReadSequence++;
callback.done();
flushCallbacks();
}
else
{
pendingCallbacks.add(new CallbackHolder(sequence, callback));
}
}
// The buffer is not sent on callback for read operations
if (bufferCallback != null && buffer != null)
{
bufferCallback.bufferDone(buffer);
}
}
finally
{
callbackLock.unlock();
}
}
private void flushCallbacks()
{
while (!pendingCallbacks.isEmpty() && pendingCallbacks.peek().sequence == nextReadSequence)
{
CallbackHolder holder = pendingCallbacks.poll();
if (holder.isError())
{
ErrorCallback error = (ErrorCallback) holder;
holder.callback.onError(error.errorCode, error.message);
}
else
{
holder.callback.done();
}
nextReadSequence++;
}
}
// Called by the JNI layer.. just ignore the
// warning
private void callbackError(final AIOCallback callback,
final long sequence,
final ByteBuffer buffer,
final int errorCode,
final String errorMessage)
{
ActiveMQJournalLogger.LOGGER.callbackError(errorMessage);
fireExceptionListener(errorCode, errorMessage);
maxIOSemaphore.release();
pendingWrites.countDown();
callbackLock.lock();
try
{
if (sequence == -1)
{
callback.onError(errorCode, errorMessage);
}
else
{
if (sequence == nextReadSequence)
{
nextReadSequence++;
callback.onError(errorCode, errorMessage);
flushCallbacks();
}
else
{
pendingCallbacks.add(new ErrorCallback(sequence, callback, errorCode, errorMessage));
}
}
}
finally
{
callbackLock.unlock();
}
// The buffer is not sent on callback for read operations
if (bufferCallback != null && buffer != null)
{
bufferCallback.bufferDone(buffer);
}
}
/**
* This is called by the native layer
*
* @param errorCode
* @param errorMessage
*/
private void fireExceptionListener(final int errorCode, final String errorMessage)
{
ActiveMQJournalLogger.LOGGER.ioError(errorCode, errorMessage);
if (ioExceptionListener != null)
{
ioExceptionListener.onIOException(ActiveMQExceptionType.getType(errorCode).createException(errorMessage), errorMessage);
}
}
private void pollEvents()
{
if (!opened)
{
return;
}
Native.internalPollEvents(handler);
}
private void startPoller()
{
writeLock.lock();
try
{
if (poller == null)
{
pollerLatch.countUp();
poller = new PollerRunnable();
try
{
pollerExecutor.execute(poller);
}
catch (Exception ex)
{
ActiveMQJournalLogger.LOGGER.errorStartingPoller(ex);
}
}
}
finally
{
writeLock.unlock();
}
}
private void checkOpened()
{
if (!opened)
{
throw new RuntimeException("File is not opened");
}
}
/**
* @throws ActiveMQException
* @throws InterruptedException
*/
private void stopPoller() throws ActiveMQException, InterruptedException
{
Native.stopPoller(handler);
// We need to make sure we won't call close until Poller is
// completely done, or we might get beautiful GPFs
pollerLatch.await();
}
public static FileLock lock(int handle)
{
if (Native.flock(handle))
{
return new ActiveMQFileLock(handle);
}
else
{
return null;
}
}
// Native ----------------------------------------------------------------------------
/**
* Explicitly adding a compare to clause that returns 0 for at least the same object.
* <br>
* If {@link Comparable#compareTo(Object)} does not return 0 -for at least the same object- some
* Collection classes methods will fail (example {@link PriorityQueue#remove(Object)}. If it
* returns 0, then {@link #equals(Object)} must return {@code true} for the exact same cases,
* otherwise we will get compatibility problems between Java5 and Java6.
*/
private static class CallbackHolder implements Comparable<CallbackHolder>
{
final long sequence;
final AIOCallback callback;
public boolean isError()
{
return false;
}
public CallbackHolder(final long sequence, final AIOCallback callback)
{
this.sequence = sequence;
this.callback = callback;
}
public int compareTo(final CallbackHolder o)
{
// It shouldn't be equals in any case
if (this == o)
return 0;
if (sequence <= o.sequence)
{
return -1;
}
else
{
return 1;
}
}
/**
* See {@link CallbackHolder}.
*/
@Override
public int hashCode()
{
return super.hashCode();
}
/**
* See {@link CallbackHolder}.
*/
@Override
public boolean equals(Object obj)
{
return super.equals(obj);
}
}
private static final class ErrorCallback extends CallbackHolder
{
final int errorCode;
final String message;
@Override
public boolean isError()
{
return true;
}
public ErrorCallback(final long sequence, final AIOCallback callback, final int errorCode, final String message)
{
super(sequence, callback);
this.errorCode = errorCode;
this.message = message;
}
/**
* See {@link CallbackHolder}.
*/
@Override
public int hashCode()
{
return super.hashCode();
}
/**
* See {@link CallbackHolder}.
*/
@Override
public boolean equals(Object obj)
{
return super.equals(obj);
}
}
private class PollerRunnable implements Runnable
{
PollerRunnable()
{
}
public void run()
{
try
{
pollEvents();
}
finally
{
// This gives us extra protection in cases of interruption
// Case the poller thread is interrupted, this will allow us to
// restart the thread when required
poller = null;
pollerLatch.countDown();
}
}
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -30,9 +30,9 @@ import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQIOErrorException; import org.apache.activemq.artemis.api.core.ActiveMQIOErrorException;
import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.core.journal.EncodingSupport;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.journal.impl.SimpleWaitIOCallback;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.buffer.TimedBuffer;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.buffer.TimedBufferObserver;
import org.apache.activemq.artemis.journal.ActiveMQJournalBundle; import org.apache.activemq.artemis.journal.ActiveMQJournalBundle;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
@ -233,7 +233,7 @@ public abstract class AbstractSequentialFile implements SequentialFile
} }
public void write(final ActiveMQBuffer bytes, final boolean sync, final IOAsyncTask callback) throws IOException public void write(final ActiveMQBuffer bytes, final boolean sync, final IOCallback callback) throws IOException
{ {
if (timedBuffer != null) if (timedBuffer != null)
{ {
@ -266,7 +266,7 @@ public abstract class AbstractSequentialFile implements SequentialFile
} }
} }
public void write(final EncodingSupport bytes, final boolean sync, final IOAsyncTask callback) public void write(final EncodingSupport bytes, final boolean sync, final IOCallback callback)
{ {
if (timedBuffer != null) if (timedBuffer != null)
{ {
@ -308,18 +308,18 @@ public abstract class AbstractSequentialFile implements SequentialFile
return file; return file;
} }
private static final class DelegateCallback implements IOAsyncTask private static final class DelegateCallback implements IOCallback
{ {
final List<IOAsyncTask> delegates; final List<IOCallback> delegates;
private DelegateCallback(final List<IOAsyncTask> delegates) private DelegateCallback(final List<IOCallback> delegates)
{ {
this.delegates = delegates; this.delegates = delegates;
} }
public void done() public void done()
{ {
for (IOAsyncTask callback : delegates) for (IOCallback callback : delegates)
{ {
try try
{ {
@ -334,7 +334,7 @@ public abstract class AbstractSequentialFile implements SequentialFile
public void onError(final int errorCode, final String errorMessage) public void onError(final int errorCode, final String errorMessage)
{ {
for (IOAsyncTask callback : delegates) for (IOCallback callback : delegates)
{ {
try try
{ {
@ -360,7 +360,7 @@ public abstract class AbstractSequentialFile implements SequentialFile
protected class LocalBufferObserver implements TimedBufferObserver protected class LocalBufferObserver implements TimedBufferObserver
{ {
public void flushBuffer(final ByteBuffer buffer, final boolean requestedSync, final List<IOAsyncTask> callbacks) public void flushBuffer(final ByteBuffer buffer, final boolean requestedSync, final List<IOCallback> callbacks)
{ {
buffer.flip(); buffer.flip();

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io;
import java.io.File; import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
@ -30,16 +30,14 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException; import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException;
import org.apache.activemq.artemis.core.journal.IOCriticalErrorListener; import org.apache.activemq.artemis.core.io.buffer.TimedBuffer;
import org.apache.activemq.artemis.core.journal.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
/** /**
* An abstract SequentialFileFactory containing basic functionality for both AIO and NIO SequentialFactories * An abstract SequentialFileFactory containing basic functionality for both AIO and NIO SequentialFactories
*/ */
abstract class AbstractSequentialFileFactory implements SequentialFileFactory public abstract class AbstractSequentialFileFactory implements SequentialFileFactory
{ {
// Timeout used to wait executors to shutdown // Timeout used to wait executors to shutdown
@ -53,6 +51,8 @@ abstract class AbstractSequentialFileFactory implements SequentialFileFactory
protected final long bufferTimeout; protected final long bufferTimeout;
protected final int maxIO;
private final IOCriticalErrorListener critialErrorListener; private final IOCriticalErrorListener critialErrorListener;
/** /**
@ -62,16 +62,17 @@ abstract class AbstractSequentialFileFactory implements SequentialFileFactory
* */ * */
protected ExecutorService writeExecutor; protected ExecutorService writeExecutor;
AbstractSequentialFileFactory(final File journalDir, protected AbstractSequentialFileFactory(final File journalDir,
final boolean buffered, final boolean buffered,
final int bufferSize, final int bufferSize,
final int bufferTimeout, final int bufferTimeout,
final int maxIO,
final boolean logRates, final boolean logRates,
final IOCriticalErrorListener criticalErrorListener) final IOCriticalErrorListener criticalErrorListener)
{ {
this.journalDir = journalDir; this.journalDir = journalDir;
if (buffered) if (buffered && bufferTimeout > 0)
{ {
timedBuffer = new TimedBuffer(bufferSize, bufferTimeout, logRates); timedBuffer = new TimedBuffer(bufferSize, bufferTimeout, logRates);
} }
@ -82,6 +83,7 @@ abstract class AbstractSequentialFileFactory implements SequentialFileFactory
this.bufferSize = bufferSize; this.bufferSize = bufferSize;
this.bufferTimeout = bufferTimeout; this.bufferTimeout = bufferTimeout;
this.critialErrorListener = criticalErrorListener; this.critialErrorListener = criticalErrorListener;
this.maxIO = maxIO;
} }
public void stop() public void stop()
@ -128,7 +130,11 @@ abstract class AbstractSequentialFileFactory implements SequentialFileFactory
true, true,
AbstractSequentialFileFactory.getThisClassLoader())); AbstractSequentialFileFactory.getThisClassLoader()));
} }
}
public int getMaxIO()
{
return maxIO;
} }
@Override @Override

View File

@ -14,11 +14,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io;
import org.apache.activemq.artemis.core.journal.impl.SyncIOCompletion;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
class DummyCallback extends SyncIOCompletion public class DummyCallback extends SyncIOCompletion
{ {
private static final DummyCallback instance = new DummyCallback(); private static final DummyCallback instance = new DummyCallback();

View File

@ -14,12 +14,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.asyncio; package org.apache.activemq.artemis.core.io;
/** /**
* The interface used for AIO Callbacks. * The interface used for AIO Callbacks.
*/ */
public interface AIOCallback public interface IOCallback
{ {
/** /**
* Method for sync notifications. When this callback method is called, there is a guarantee the data is written on the disk. * Method for sync notifications. When this callback method is called, there is a guarantee the data is written on the disk.

View File

@ -14,8 +14,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal; package org.apache.activemq.artemis.core.io;
/**
* TODO Merge this with IOExceptionListener
*/
public interface IOCriticalErrorListener public interface IOCriticalErrorListener
{ {
void onIOException(Exception code, String message, SequentialFile file); void onIOException(Exception code, String message, SequentialFile file);

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.asyncio; package org.apache.activemq.artemis.core.io;
public interface IOExceptionListener public interface IOExceptionListener
{ {

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal; package org.apache.activemq.artemis.core.io;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -22,19 +22,18 @@ import java.nio.ByteBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.core.journal.impl.TimedBuffer; import org.apache.activemq.artemis.core.journal.EncodingSupport;
import org.apache.activemq.artemis.core.io.buffer.TimedBuffer;
public interface SequentialFile public interface SequentialFile
{ {
/*
* Creates the file if it doesn't already exist, then opens it
*/
void open() throws Exception;
boolean isOpen(); boolean isOpen();
boolean exists(); boolean exists();
void open() throws Exception;
/** /**
* The maximum number of simultaneous writes accepted * The maximum number of simultaneous writes accepted
* @param maxIO * @param maxIO
@ -50,15 +49,15 @@ public interface SequentialFile
String getFileName(); String getFileName();
void fill(int position, int size, byte fillCharacter) throws Exception; void fill(int size) throws Exception;
void delete() throws IOException, InterruptedException, ActiveMQException; void delete() throws IOException, InterruptedException, ActiveMQException;
void write(ActiveMQBuffer bytes, boolean sync, IOAsyncTask callback) throws Exception; void write(ActiveMQBuffer bytes, boolean sync, IOCallback callback) throws Exception;
void write(ActiveMQBuffer bytes, boolean sync) throws Exception; void write(ActiveMQBuffer bytes, boolean sync) throws Exception;
void write(EncodingSupport bytes, boolean sync, IOAsyncTask callback) throws Exception; void write(EncodingSupport bytes, boolean sync, IOCallback callback) throws Exception;
void write(EncodingSupport bytes, boolean sync) throws Exception; void write(EncodingSupport bytes, boolean sync) throws Exception;
@ -68,10 +67,10 @@ public interface SequentialFile
* NIO). To be safe, use a buffer from the corresponding * NIO). To be safe, use a buffer from the corresponding
* {@link SequentialFileFactory#newBuffer(int)}. * {@link SequentialFileFactory#newBuffer(int)}.
*/ */
void writeDirect(ByteBuffer bytes, boolean sync, IOAsyncTask callback); void writeDirect(ByteBuffer bytes, boolean sync, IOCallback callback);
/** /**
* Write directly to the file without using any buffer * Write directly to the file without using intermediate any buffer
* @param bytes the ByteBuffer must be compatible with the SequentialFile implementation (AIO or * @param bytes the ByteBuffer must be compatible with the SequentialFile implementation (AIO or
* NIO). To be safe, use a buffer from the corresponding * NIO). To be safe, use a buffer from the corresponding
* {@link SequentialFileFactory#newBuffer(int)}. * {@link SequentialFileFactory#newBuffer(int)}.
@ -79,21 +78,11 @@ public interface SequentialFile
void writeDirect(ByteBuffer bytes, boolean sync) throws Exception; void writeDirect(ByteBuffer bytes, boolean sync) throws Exception;
/** /**
* Write directly to the file. This is used by compacting and other places where we write a big
* buffer in a single shot. writeInternal should always block until the entire write is sync on
* disk.
* @param bytes the ByteBuffer must be compatible with the SequentialFile implementation (AIO or * @param bytes the ByteBuffer must be compatible with the SequentialFile implementation (AIO or
* NIO). To be safe, use a buffer from the corresponding * NIO). To be safe, use a buffer from the corresponding
* {@link SequentialFileFactory#newBuffer(int)}. * {@link SequentialFileFactory#newBuffer(int)}.
*/ */
void writeInternal(ByteBuffer bytes) throws Exception; int read(ByteBuffer bytes, IOCallback callback) throws Exception;
/**
* @param bytes the ByteBuffer must be compatible with the SequentialFile implementation (AIO or
* NIO). To be safe, use a buffer from the corresponding
* {@link SequentialFileFactory#newBuffer(int)}.
*/
int read(ByteBuffer bytes, IOAsyncTask callback) throws Exception;
/** /**
* @param bytes the ByteBuffer must be compatible with the SequentialFile implementation (AIO or * @param bytes the ByteBuffer must be compatible with the SequentialFile implementation (AIO or
@ -108,8 +97,6 @@ public interface SequentialFile
void close() throws Exception; void close() throws Exception;
void waitForClose() throws Exception;
void sync() throws IOException; void sync() throws IOException;
long size() throws Exception; long size() throws Exception;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal; package org.apache.activemq.artemis.core.io;
import java.io.File; import java.io.File;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -26,7 +26,9 @@ import java.util.List;
*/ */
public interface SequentialFileFactory public interface SequentialFileFactory
{ {
SequentialFile createSequentialFile(String fileName, int maxIO); SequentialFile createSequentialFile(String fileName);
int getMaxIO();
/** /**
* Lists files that end with the given extension. * Lists files that end with the given extension.

View File

@ -14,49 +14,64 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io.aio;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.PriorityQueue;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.core.asyncio.AsynchronousFile; import org.apache.activemq.artemis.api.core.ActiveMQNativeIOError;
import org.apache.activemq.artemis.core.asyncio.BufferCallback; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.asyncio.IOExceptionListener; import org.apache.activemq.artemis.core.io.AbstractSequentialFile;
import org.apache.activemq.artemis.core.asyncio.impl.AsynchronousFileImpl; import org.apache.activemq.artemis.core.io.DummyCallback;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.journal.impl.SimpleWaitIOCallback;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.jlibaio.LibaioFile;
import org.apache.activemq.artemis.utils.ReusableLatch;
public class AIOSequentialFile extends AbstractSequentialFile implements IOExceptionListener public class AIOSequentialFile extends AbstractSequentialFile
{ {
private boolean opened = false; private boolean opened = false;
private final int maxIO; private LibaioFile aioFile;
private AsynchronousFile aioFile; private final AIOSequentialFileFactory aioFactory;
private final BufferCallback bufferCallback; private final ReusableLatch pendingCallbacks = new ReusableLatch();
/** The pool for Thread pollers */ /**
private final Executor pollerExecutor; * Used to determine the next writing sequence
*/
private final AtomicLong nextWritingSequence = new AtomicLong(0);
public AIOSequentialFile(final SequentialFileFactory factory, /**
* AIO can't guarantee ordering over callbacks.
* <br>
* We use this {@link PriorityQueue} to hold values until they are in order
*/
final PriorityQueue<AIOSequentialFileFactory.AIOSequentialCallback> pendingCallbackList = new PriorityQueue<>();
/**
* Used to determine the next writing sequence.
* This is accessed from a single thread (the Poller Thread)
*/
private long nextReadSequence = 0;
public AIOSequentialFile(final AIOSequentialFileFactory factory,
final int bufferSize, final int bufferSize,
final long bufferTimeoutMilliseconds, final long bufferTimeoutMilliseconds,
final File directory, final File directory,
final String fileName, final String fileName,
final int maxIO, final Executor writerExecutor)
final BufferCallback bufferCallback,
final Executor writerExecutor,
final Executor pollerExecutor)
{ {
super(directory, fileName, factory, writerExecutor); super(directory, fileName, factory, writerExecutor);
this.maxIO = maxIO; this.aioFactory = factory;
this.bufferCallback = bufferCallback;
this.pollerExecutor = pollerExecutor;
} }
public boolean isOpen() public boolean isOpen()
@ -82,15 +97,12 @@ public class AIOSequentialFile extends AbstractSequentialFile implements IOExcep
public SequentialFile cloneFile() public SequentialFile cloneFile()
{ {
return new AIOSequentialFile(factory, return new AIOSequentialFile(aioFactory,
-1, -1,
-1, -1,
getFile().getParentFile(), getFile().getParentFile(),
getFile().getName(), getFile().getName(),
maxIO, writerExecutor);
bufferCallback,
writerExecutor,
pollerExecutor);
} }
@Override @Override
@ -103,115 +115,73 @@ public class AIOSequentialFile extends AbstractSequentialFile implements IOExcep
super.close(); super.close();
if (!pendingCallbacks.await(10, TimeUnit.SECONDS))
{
factory.onIOError(new IOException("Timeout on close"), "Timeout on close", this);
}
opened = false; opened = false;
timedBuffer = null; timedBuffer = null;
aioFile.close(); aioFile.close();
aioFile = null; aioFile = null;
notifyAll();
} }
@Override
public synchronized void waitForClose() throws Exception
{
while (isOpen())
{
wait();
}
}
public void fill(final int position, final int size, final byte fillCharacter) throws Exception public synchronized void fill(final int size) throws Exception
{ {
checkOpened(); checkOpened();
aioFile.fill(size);
int fileblockSize = aioFile.getBlockSize(); fileSize = aioFile.getSize();
int blockSize = fileblockSize;
if (size % (100 * 1024 * 1024) == 0)
{
blockSize = 100 * 1024 * 1024;
}
else if (size % (10 * 1024 * 1024) == 0)
{
blockSize = 10 * 1024 * 1024;
}
else if (size % (1024 * 1024) == 0)
{
blockSize = 1024 * 1024;
}
else if (size % (10 * 1024) == 0)
{
blockSize = 10 * 1024;
}
else
{
blockSize = fileblockSize;
}
int blocks = size / blockSize;
if (size % blockSize != 0)
{
blocks++;
}
int filePosition = position;
if (position % fileblockSize != 0)
{
filePosition = (position / fileblockSize + 1) * fileblockSize;
}
aioFile.fill(filePosition, blocks, blockSize, fillCharacter);
fileSize = aioFile.size();
} }
public void open() throws Exception public void open() throws Exception
{ {
open(maxIO, true); open(aioFactory.getMaxIO(), true);
} }
public synchronized void open(final int maxIO, final boolean useExecutor) throws ActiveMQException public synchronized void open(final int maxIO, final boolean useExecutor) throws ActiveMQException
{ {
opened = true; opened = true;
aioFile = new AsynchronousFileImpl(useExecutor ? writerExecutor : null, pollerExecutor, this);
try try
{ {
aioFile.open(getFile().getAbsolutePath(), maxIO); aioFile = aioFactory.libaioContext.openFile(getFile(), true);
} }
catch (ActiveMQException e) catch (IOException e)
{ {
factory.onIOError(e, e.getMessage(), this); factory.onIOError(e, e.getMessage(), this);
throw e; throw new ActiveMQNativeIOError(e.getMessage(), e);
} }
position.set(0); position.set(0);
aioFile.setBufferCallback(bufferCallback); fileSize = aioFile.getSize();
fileSize = aioFile.size();
} }
public void setBufferCallback(final BufferCallback callback) public int read(final ByteBuffer bytes, final IOCallback callback) throws ActiveMQException
{
aioFile.setBufferCallback(callback);
}
public int read(final ByteBuffer bytes, final IOAsyncTask callback) throws ActiveMQException
{ {
checkOpened();
int bytesToRead = bytes.limit(); int bytesToRead = bytes.limit();
long positionToRead = position.getAndAdd(bytesToRead); long positionToRead = position.getAndAdd(bytesToRead);
bytes.rewind(); bytes.rewind();
aioFile.read(positionToRead, bytesToRead, bytes, callback); try
{
// We don't send the buffer to the callback on read,
// because we want the buffer available.
// Sending it through the callback would make it released
aioFile.read(positionToRead, bytesToRead, bytes, getCallback(callback, null));
}
catch (IOException e)
{
factory.onIOError(e, e.getMessage(), this);
throw new ActiveMQNativeIOError(e.getMessage(), e);
}
return bytesToRead; return bytesToRead;
} }
@ -227,39 +197,6 @@ public class AIOSequentialFile extends AbstractSequentialFile implements IOExcep
return bytesRead; return bytesRead;
} }
public void sync()
{
throw new UnsupportedOperationException("This method is not supported on AIO");
}
public long size() throws Exception
{
if (aioFile == null)
{
return getFile().length();
}
else
{
return aioFile.size();
}
}
@Override
public String toString()
{
return "AIOSequentialFile:" + getFile().getAbsolutePath();
}
// Public methods
// -----------------------------------------------------------------------------------------------------
@Override
public void onIOException(Exception code, String message)
{
factory.onIOError(code, message, this);
}
public void writeDirect(final ByteBuffer bytes, final boolean sync) throws Exception public void writeDirect(final ByteBuffer bytes, final boolean sync) throws Exception
{ {
if (sync) if (sync)
@ -278,24 +215,94 @@ public class AIOSequentialFile extends AbstractSequentialFile implements IOExcep
/** /**
* *
* @param sync Not used on AIO * Note: Parameter sync is not used on AIO
* */ * */
public void writeDirect(final ByteBuffer bytes, final boolean sync, final IOAsyncTask callback) public void writeDirect(final ByteBuffer bytes, final boolean sync, final IOCallback callback)
{ {
checkOpened();
final int bytesToWrite = factory.calculateBlockSize(bytes.limit()); final int bytesToWrite = factory.calculateBlockSize(bytes.limit());
final long positionToWrite = position.getAndAdd(bytesToWrite); final long positionToWrite = position.getAndAdd(bytesToWrite);
aioFile.write(positionToWrite, bytesToWrite, bytes, callback); AIOSequentialFileFactory.AIOSequentialCallback runnableCallback = getCallback(callback, bytes);
runnableCallback.initWrite(positionToWrite, bytesToWrite);
if (writerExecutor != null)
{
writerExecutor.execute(runnableCallback);
}
else
{
runnableCallback.run();
}
} }
public void writeInternal(final ByteBuffer bytes) throws ActiveMQException
AIOSequentialFileFactory.AIOSequentialCallback getCallback(IOCallback originalCallback, ByteBuffer buffer)
{ {
final int bytesToWrite = factory.calculateBlockSize(bytes.limit()); AIOSequentialFileFactory.AIOSequentialCallback callback = aioFactory.getCallback();
callback.init(this.nextWritingSequence.getAndIncrement(), originalCallback, aioFile, this, buffer);
pendingCallbacks.countUp();
return callback;
}
final long positionToWrite = position.getAndAdd(bytesToWrite);
aioFile.writeInternal(positionToWrite, bytesToWrite, bytes); void done(AIOSequentialFileFactory.AIOSequentialCallback callback)
{
if (callback.writeSequence == -1)
{
callback.sequentialDone();
pendingCallbacks.countDown();
}
if (callback.writeSequence == nextReadSequence)
{
nextReadSequence++;
callback.sequentialDone();
pendingCallbacks.countDown();
flushCallbacks();
}
else
{
pendingCallbackList.add(callback);
}
}
private void flushCallbacks()
{
while (!pendingCallbackList.isEmpty() && pendingCallbackList.peek().writeSequence == nextReadSequence)
{
AIOSequentialFileFactory.AIOSequentialCallback callback = pendingCallbackList.poll();
callback.sequentialDone();
nextReadSequence++;
pendingCallbacks.countDown();
}
}
public void sync()
{
throw new UnsupportedOperationException("This method is not supported on AIO");
}
public long size() throws Exception
{
if (aioFile == null)
{
return getFile().length();
}
else
{
return aioFile.getSize();
}
}
@Override
public String toString()
{
return "AIOSequentialFile:" + getFile().getAbsolutePath();
} }
// Protected methods // Protected methods
@ -319,7 +326,7 @@ public class AIOSequentialFile extends AbstractSequentialFile implements IOExcep
{ {
if (aioFile == null || !opened) if (aioFile == null || !opened)
{ {
throw new IllegalStateException("File not opened"); throw new NullPointerException("File not opened, file=null");
} }
} }

View File

@ -14,9 +14,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io.aio;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
@ -24,13 +25,18 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException; import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException;
import org.apache.activemq.artemis.core.asyncio.BufferCallback; import org.apache.activemq.artemis.core.io.AbstractSequentialFileFactory;
import org.apache.activemq.artemis.core.asyncio.impl.AsynchronousFileImpl; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.IOCriticalErrorListener; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.libaio.Native; import org.apache.activemq.artemis.core.journal.impl.JournalConstants;
import org.apache.activemq.artemis.jlibaio.LibaioContext;
import org.apache.activemq.artemis.jlibaio.LibaioFile;
import org.apache.activemq.artemis.jlibaio.SubmitInfo;
import org.apache.activemq.artemis.jlibaio.util.CallbackCache;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
@ -40,8 +46,16 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
private final ReuseBuffersController buffersControl = new ReuseBuffersController(); private final ReuseBuffersController buffersControl = new ReuseBuffersController();
private volatile boolean reuseBuffers = true;
private ExecutorService pollerExecutor; private ExecutorService pollerExecutor;
volatile LibaioContext<AIOSequentialCallback> libaioContext;
private final CallbackCache<AIOSequentialCallback> callbackPool;
private final AtomicBoolean running = new AtomicBoolean(false);
// This method exists just to make debug easier. // This method exists just to make debug easier.
// I could replace log.trace by log.info temporarily while I was debugging // I could replace log.trace by log.info temporarily while I was debugging
// Journal // Journal
@ -50,20 +64,22 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
ActiveMQJournalLogger.LOGGER.trace(message); ActiveMQJournalLogger.LOGGER.trace(message);
} }
public AIOSequentialFileFactory(final File journalDir) public AIOSequentialFileFactory(final File journalDir, int maxIO)
{ {
this(journalDir, this(journalDir,
JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_AIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_AIO,
JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_AIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_AIO,
maxIO,
false, false,
null); null);
} }
public AIOSequentialFileFactory(final File journalDir, final IOCriticalErrorListener listener) public AIOSequentialFileFactory(final File journalDir, final IOCriticalErrorListener listener, int maxIO)
{ {
this(journalDir, this(journalDir,
JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_AIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_AIO,
JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_AIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_AIO,
maxIO,
false, false,
listener); listener);
} }
@ -71,31 +87,53 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
public AIOSequentialFileFactory(final File journalDir, public AIOSequentialFileFactory(final File journalDir,
final int bufferSize, final int bufferSize,
final int bufferTimeout, final int bufferTimeout,
final int maxIO,
final boolean logRates) final boolean logRates)
{ {
this(journalDir, bufferSize, bufferTimeout, logRates, null); this(journalDir, bufferSize, bufferTimeout, maxIO, logRates, null);
} }
public AIOSequentialFileFactory(final File journalDir, public AIOSequentialFileFactory(final File journalDir,
final int bufferSize, final int bufferSize,
final int bufferTimeout, final int bufferTimeout,
final int maxIO,
final boolean logRates, final boolean logRates,
final IOCriticalErrorListener listener) final IOCriticalErrorListener listener)
{ {
super(journalDir, true, bufferSize, bufferTimeout, logRates, listener); super(journalDir, true, bufferSize, bufferTimeout, maxIO, logRates, listener);
callbackPool = new CallbackCache<>(maxIO);
} }
public SequentialFile createSequentialFile(final String fileName, final int maxIO) public AIOSequentialCallback getCallback()
{
AIOSequentialCallback callback = callbackPool.get();
if (callback == null)
{
callback = new AIOSequentialCallback();
}
return callback;
}
public void enableBufferReuse()
{
this.reuseBuffers = true;
}
public void disableBufferReuse()
{
this.reuseBuffers = false;
}
public SequentialFile createSequentialFile(final String fileName)
{ {
return new AIOSequentialFile(this, return new AIOSequentialFile(this,
bufferSize, bufferSize,
bufferTimeout, bufferTimeout,
journalDir, journalDir,
fileName, fileName,
maxIO, writeExecutor);
buffersControl.callback,
writeExecutor,
pollerExecutor);
} }
public boolean isSupportsCallbacks() public boolean isSupportsCallbacks()
@ -105,7 +143,7 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
public static boolean isSupported() public static boolean isSupported()
{ {
return AsynchronousFileImpl.isLoaded(); return LibaioContext.isLoaded();
} }
public ByteBuffer allocateDirectBuffer(final int size) public ByteBuffer allocateDirectBuffer(final int size)
@ -118,7 +156,7 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
} }
// The buffer on AIO has to be a multiple of 512 // The buffer on AIO has to be a multiple of 512
ByteBuffer buffer = AsynchronousFileImpl.newBuffer(blocks * 512); ByteBuffer buffer = LibaioContext.newAlignedBuffer(blocks * 512, 512);
buffer.limit(size); buffer.limit(size);
@ -127,7 +165,7 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
public void releaseDirectBuffer(final ByteBuffer buffer) public void releaseDirectBuffer(final ByteBuffer buffer)
{ {
Native.destroyBuffer(buffer); LibaioContext.freeBuffer(buffer);
} }
public ByteBuffer newBuffer(int size) public ByteBuffer newBuffer(int size)
@ -142,7 +180,8 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
public void clearBuffer(final ByteBuffer directByteBuffer) public void clearBuffer(final ByteBuffer directByteBuffer)
{ {
AsynchronousFileImpl.clearBuffer(directByteBuffer); directByteBuffer.position(0);
libaioContext.memsetBuffer(directByteBuffer);
} }
public int getAlignment() public int getAlignment()
@ -168,48 +207,63 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.apache.activemq.artemis.core.journal.SequentialFileFactory#releaseBuffer(java.nio.ByteBuffer) * @see org.apache.activemq.artemis.core.io.SequentialFileFactory#releaseBuffer(java.nio.ByteBuffer)
*/ */
@Override @Override
public synchronized void releaseBuffer(final ByteBuffer buffer) public synchronized void releaseBuffer(final ByteBuffer buffer)
{ {
Native.destroyBuffer(buffer); LibaioContext.freeBuffer(buffer);
} }
@Override @Override
public void start() public void start()
{ {
super.start(); if (running.compareAndSet(false, true))
{
super.start();
pollerExecutor = Executors.newCachedThreadPool(new ActiveMQThreadFactory("ActiveMQ-AIO-poller-pool" + System.identityHashCode(this), this.libaioContext = new LibaioContext(maxIO, true);
true,
AIOSequentialFileFactory.getThisClassLoader())); this.running.set(true);
pollerExecutor = Executors.newCachedThreadPool(new ActiveMQThreadFactory("ActiveMQ-AIO-poller-pool" + System.identityHashCode(this),
true,
AIOSequentialFileFactory.getThisClassLoader()));
pollerExecutor.execute(new PollerRunnable());
}
} }
@Override @Override
public void stop() public void stop()
{ {
buffersControl.stop(); if (this.running.compareAndSet(true, false))
if (pollerExecutor != null)
{ {
pollerExecutor.shutdown(); buffersControl.stop();
try libaioContext.close();
libaioContext = null;
if (pollerExecutor != null)
{ {
if (!pollerExecutor.awaitTermination(AbstractSequentialFileFactory.EXECUTOR_TIMEOUT, TimeUnit.SECONDS)) pollerExecutor.shutdown();
try
{ {
ActiveMQJournalLogger.LOGGER.timeoutOnPollerShutdown(new Exception("trace")); if (!pollerExecutor.awaitTermination(AbstractSequentialFileFactory.EXECUTOR_TIMEOUT, TimeUnit.SECONDS))
{
ActiveMQJournalLogger.LOGGER.timeoutOnPollerShutdown(new Exception("trace"));
}
}
catch (InterruptedException e)
{
throw new ActiveMQInterruptedException(e);
} }
} }
catch (InterruptedException e)
{
throw new ActiveMQInterruptedException(e);
}
}
super.stop(); super.stop();
}
} }
@Override @Override
@ -218,6 +272,135 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
stop(); stop();
} }
/**
* The same callback is used for Runnable executor.
* This way we can save some memory over the pool.
*/
public class AIOSequentialCallback implements SubmitInfo, Runnable, Comparable<AIOSequentialCallback>
{
IOCallback callback;
boolean error = false;
AIOSequentialFile sequentialFile;
ByteBuffer buffer;
LibaioFile libaioFile;
String errorMessage;
int errorCode = -1;
long writeSequence;
long position;
int bytes;
@Override
public String toString()
{
return "AIOSequentialCallback{" +
"error=" + error +
", errorMessage='" + errorMessage + '\'' +
", errorCode=" + errorCode +
", writeSequence=" + writeSequence +
", position=" + position +
'}';
}
public AIOSequentialCallback initWrite(long positionToWrite, int bytesToWrite)
{
this.position = positionToWrite;
this.bytes = bytesToWrite;
return this;
}
public void run()
{
try
{
libaioFile.write(position, bytes, buffer, this);
}
catch (IOException e)
{
callback.onError(-1, e.getMessage());
}
}
public int compareTo(AIOSequentialCallback other)
{
if (this == other || this.writeSequence == other.writeSequence)
{
return 0;
}
else if (other.writeSequence < this.writeSequence)
{
return 1;
}
else
{
return -1;
}
}
public AIOSequentialCallback init(long writeSequence, IOCallback IOCallback, LibaioFile libaioFile, AIOSequentialFile sequentialFile, ByteBuffer usedBuffer)
{
this.callback = IOCallback;
this.sequentialFile = sequentialFile;
this.error = false;
this.buffer = usedBuffer;
this.libaioFile = libaioFile;
this.writeSequence = writeSequence;
this.errorMessage = null;
return this;
}
@Override
public void onError(int errno, String message)
{
this.error = true;
this.errorCode = errno;
this.errorMessage = message;
}
/**
* this is called by libaio.
*/
public void done()
{
this.sequentialFile.done(this);
}
/**
* This is callbed by the AIOSequentialFile, after determined the callbacks were returned in sequence
*/
public void sequentialDone()
{
if (error)
{
callback.onError(errorCode, errorMessage);
errorMessage = null;
}
else
{
if (callback != null)
{
callback.done();
}
if (buffer != null && reuseBuffers)
{
buffersControl.bufferDone(buffer);
}
callbackPool.put(AIOSequentialCallback.this);
}
}
}
private class PollerRunnable implements Runnable
{
public void run()
{
libaioContext.poll();
}
}
/** /**
* Class that will control buffer-reuse * Class that will control buffer-reuse
*/ */
@ -225,17 +408,10 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
{ {
private volatile long bufferReuseLastTime = System.currentTimeMillis(); private volatile long bufferReuseLastTime = System.currentTimeMillis();
/**
* This queue is fed by {@link org.apache.activemq.artemis.core.journal.impl.AIOSequentialFileFactory.ReuseBuffersController.LocalBufferCallback}
* which is called directly by NIO or NIO. On the case of the AIO this is almost called by the native layer as
* soon as the buffer is not being used any more and ready to be reused or GCed
*/
private final ConcurrentLinkedQueue<ByteBuffer> reuseBuffersQueue = new ConcurrentLinkedQueue<ByteBuffer>(); private final ConcurrentLinkedQueue<ByteBuffer> reuseBuffersQueue = new ConcurrentLinkedQueue<ByteBuffer>();
private boolean stopped = false; private boolean stopped = false;
final BufferCallback callback = new LocalBufferCallback();
public ByteBuffer newBuffer(final int size) public ByteBuffer newBuffer(final int size)
{ {
// if a new buffer wasn't requested in 10 seconds, we clear the queue // if a new buffer wasn't requested in 10 seconds, we clear the queue
@ -258,7 +434,7 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
// buffer. // buffer.
if (size > bufferSize) if (size > bufferSize)
{ {
return AsynchronousFileImpl.newBuffer(size); return LibaioContext.newAlignedBuffer(size, 512);
} }
else else
{ {
@ -272,7 +448,7 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
if (buffer == null) if (buffer == null)
{ {
// if empty create a new one. // if empty create a new one.
buffer = AsynchronousFileImpl.newBuffer(bufferSize); buffer = LibaioContext.newAlignedBuffer(size, 512);
buffer.limit(alignedSize); buffer.limit(alignedSize);
} }
@ -306,31 +482,28 @@ public final class AIOSequentialFileFactory extends AbstractSequentialFileFactor
} }
} }
private class LocalBufferCallback implements BufferCallback public void bufferDone(final ByteBuffer buffer)
{ {
public void bufferDone(final ByteBuffer buffer) synchronized (this)
{ {
synchronized (ReuseBuffersController.this)
{
if (stopped) if (stopped)
{
releaseBuffer(buffer);
}
else
{
bufferReuseLastTime = System.currentTimeMillis();
// If a buffer has any other than the configured bufferSize, the buffer
// will be just sent to GC
if (buffer.capacity() == bufferSize)
{ {
releaseBuffer(buffer); reuseBuffersQueue.offer(buffer);
} }
else else
{ {
bufferReuseLastTime = System.currentTimeMillis(); releaseBuffer(buffer);
// If a buffer has any other than the configured bufferSize, the buffer
// will be just sent to GC
if (buffer.capacity() == bufferSize)
{
reuseBuffersQueue.offer(buffer);
}
else
{
releaseBuffer(buffer);
}
} }
} }
} }

View File

@ -14,23 +14,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.asyncio.impl; package org.apache.activemq.artemis.core.io.aio;
import java.io.IOException; import java.io.IOException;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.nio.channels.FileLock; import java.nio.channels.FileLock;
import org.apache.activemq.artemis.core.libaio.Native; import org.apache.activemq.artemis.jlibaio.LibaioFile;
public class ActiveMQFileLock extends FileLock public class ActiveMQFileLock extends FileLock
{ {
private final int handle; private final LibaioFile file;
protected ActiveMQFileLock(final int handle) public ActiveMQFileLock(final LibaioFile handle)
{ {
super((FileChannel)null, 0, 0, false); super((FileChannel)null, 0, 0, false);
this.handle = handle; this.file = handle;
} }
@Override @Override
@ -42,6 +42,6 @@ public class ActiveMQFileLock extends FileLock
@Override @Override
public void release() throws IOException public void release() throws IOException
{ {
Native.closeFile(handle); file.close();
} }
} }

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io.buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -28,8 +28,8 @@ import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException; import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.core.journal.EncodingSupport;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding; import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
@ -57,7 +57,7 @@ public class TimedBuffer
private int bufferLimit = 0; private int bufferLimit = 0;
private List<IOAsyncTask> callbacks; private List<IOCallback> callbacks;
private volatile int timeout; private volatile int timeout;
@ -114,7 +114,7 @@ public class TimedBuffer
bufferLimit = 0; bufferLimit = 0;
callbacks = new ArrayList<IOAsyncTask>(); callbacks = new ArrayList<IOCallback>();
this.timeout = timeout; this.timeout = timeout;
} }
@ -260,12 +260,12 @@ public class TimedBuffer
} }
} }
public synchronized void addBytes(final ActiveMQBuffer bytes, final boolean sync, final IOAsyncTask callback) public synchronized void addBytes(final ActiveMQBuffer bytes, final boolean sync, final IOCallback callback)
{ {
addBytes(new ByteArrayEncoding(bytes.toByteBuffer().array()), sync, callback); addBytes(new ByteArrayEncoding(bytes.toByteBuffer().array()), sync, callback);
} }
public synchronized void addBytes(final EncodingSupport bytes, final boolean sync, final IOAsyncTask callback) public synchronized void addBytes(final EncodingSupport bytes, final boolean sync, final IOCallback callback)
{ {
if (!started) if (!started)
{ {
@ -329,7 +329,7 @@ public class TimedBuffer
pendingSync = false; pendingSync = false;
// swap the instance as the previous callback list is being used asynchronously // swap the instance as the previous callback list is being used asynchronously
callbacks = new LinkedList<IOAsyncTask>(); callbacks = new LinkedList<IOCallback>();
buffer.clear(); buffer.clear();

View File

@ -14,12 +14,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io.buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
public interface TimedBufferObserver public interface TimedBufferObserver
{ {
@ -34,7 +35,7 @@ public interface TimedBufferObserver
// Public -------------------------------------------------------- // Public --------------------------------------------------------
void flushBuffer(ByteBuffer buffer, boolean syncRequested, List<IOAsyncTask> callbacks); void flushBuffer(ByteBuffer buffer, boolean syncRequested, List<IOCallback> callbacks);
/** Return the number of remaining bytes that still fit on the observer (file) */ /** Return the number of remaining bytes that still fit on the observer (file) */
int getRemainingBytes(); int getRemainingBytes();

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io.nio;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -29,9 +29,10 @@ import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
import org.apache.activemq.artemis.api.core.ActiveMQIOErrorException; import org.apache.activemq.artemis.api.core.ActiveMQIOErrorException;
import org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException; import org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.AbstractSequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.journal.ActiveMQJournalBundle; import org.apache.activemq.artemis.journal.ActiveMQJournalBundle;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
@ -107,20 +108,16 @@ public final class NIOSequentialFile extends AbstractSequentialFile
} }
} }
public void fill(final int position, final int size, final byte fillCharacter) throws IOException public void fill(final int size) throws IOException
{ {
ByteBuffer bb = ByteBuffer.allocate(size); ByteBuffer bb = ByteBuffer.allocate(size);
for (int i = 0; i < size; i++) bb.limit(size);
{ bb.position(0);
bb.put(fillCharacter);
}
bb.flip();
try try
{ {
channel.position(position); channel.position(0);
channel.write(bb); channel.write(bb);
channel.force(false); channel.force(false);
channel.position(0); channel.position(0);
@ -134,14 +131,6 @@ public final class NIOSequentialFile extends AbstractSequentialFile
fileSize = channel.size(); fileSize = channel.size();
} }
public synchronized void waitForClose() throws InterruptedException
{
while (isOpen())
{
wait();
}
}
@Override @Override
public synchronized void close() throws IOException, InterruptedException, ActiveMQException public synchronized void close() throws IOException, InterruptedException, ActiveMQException
{ {
@ -185,7 +174,7 @@ public final class NIOSequentialFile extends AbstractSequentialFile
return read(bytes, null); return read(bytes, null);
} }
public synchronized int read(final ByteBuffer bytes, final IOAsyncTask callback) throws IOException, public synchronized int read(final ByteBuffer bytes, final IOCallback callback) throws IOException,
ActiveMQIllegalStateException ActiveMQIllegalStateException
{ {
try try
@ -278,7 +267,7 @@ public final class NIOSequentialFile extends AbstractSequentialFile
return new NIOSequentialFile(factory, directory, getFileName(), maxIO, writerExecutor); return new NIOSequentialFile(factory, directory, getFileName(), maxIO, writerExecutor);
} }
public void writeDirect(final ByteBuffer bytes, final boolean sync, final IOAsyncTask callback) public void writeDirect(final ByteBuffer bytes, final boolean sync, final IOCallback callback)
{ {
if (callback == null) if (callback == null)
{ {
@ -315,7 +304,7 @@ public final class NIOSequentialFile extends AbstractSequentialFile
return super.newBuffer(size, limit); return super.newBuffer(size, limit);
} }
private void internalWrite(final ByteBuffer bytes, final boolean sync, final IOAsyncTask callback) throws IOException, ActiveMQIOErrorException, InterruptedException private void internalWrite(final ByteBuffer bytes, final boolean sync, final IOCallback callback) throws IOException, ActiveMQIOErrorException, InterruptedException
{ {
if (!isOpen()) if (!isOpen())
{ {
@ -387,7 +376,7 @@ public final class NIOSequentialFile extends AbstractSequentialFile
* @throws IOException * @throws IOException
* @throws Exception * @throws Exception
*/ */
private void doInternalWrite(final ByteBuffer bytes, final boolean sync, final IOAsyncTask callback) throws IOException private void doInternalWrite(final ByteBuffer bytes, final boolean sync, final IOCallback callback) throws IOException
{ {
channel.write(bytes); channel.write(bytes);

View File

@ -14,45 +14,49 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.io.nio;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.apache.activemq.artemis.core.journal.IOCriticalErrorListener; import org.apache.activemq.artemis.core.io.AbstractSequentialFileFactory;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener;
import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.impl.JournalConstants;
public class NIOSequentialFileFactory extends AbstractSequentialFileFactory public class NIOSequentialFileFactory extends AbstractSequentialFileFactory
{ {
public NIOSequentialFileFactory(final File journalDir) public NIOSequentialFileFactory(final File journalDir, final int maxIO)
{ {
this(journalDir, null); this(journalDir, null, maxIO);
} }
public NIOSequentialFileFactory(final File journalDir, final IOCriticalErrorListener listener) public NIOSequentialFileFactory(final File journalDir, final IOCriticalErrorListener listener, final int maxIO)
{ {
this(journalDir, this(journalDir,
false, false,
JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_NIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_NIO,
JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_NIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_NIO,
maxIO,
false, false,
listener); listener);
} }
public NIOSequentialFileFactory(final File journalDir, final boolean buffered) public NIOSequentialFileFactory(final File journalDir, final boolean buffered, final int maxIO)
{ {
this(journalDir, buffered, null); this(journalDir, buffered, null, maxIO);
} }
public NIOSequentialFileFactory(final File journalDir, public NIOSequentialFileFactory(final File journalDir,
final boolean buffered, final boolean buffered,
final IOCriticalErrorListener listener) final IOCriticalErrorListener listener, final int maxIO)
{ {
this(journalDir, this(journalDir,
buffered, buffered,
JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_NIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_SIZE_NIO,
JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_NIO, JournalConstants.DEFAULT_JOURNAL_BUFFER_TIMEOUT_NIO,
maxIO,
false, false,
listener); listener);
} }
@ -61,29 +65,25 @@ public class NIOSequentialFileFactory extends AbstractSequentialFileFactory
final boolean buffered, final boolean buffered,
final int bufferSize, final int bufferSize,
final int bufferTimeout, final int bufferTimeout,
final int maxIO,
final boolean logRates) final boolean logRates)
{ {
this(journalDir, buffered, bufferSize, bufferTimeout, logRates, null); this(journalDir, buffered, bufferSize, bufferTimeout, maxIO, logRates, null);
} }
public NIOSequentialFileFactory(final File journalDir, public NIOSequentialFileFactory(final File journalDir,
final boolean buffered, final boolean buffered,
final int bufferSize, final int bufferSize,
final int bufferTimeout, final int bufferTimeout,
final int maxIO,
final boolean logRates, final boolean logRates,
final IOCriticalErrorListener listener) final IOCriticalErrorListener listener)
{ {
super(journalDir, buffered, bufferSize, bufferTimeout, logRates, listener); super(journalDir, buffered, bufferSize, bufferTimeout, maxIO, logRates, listener);
} }
public SequentialFile createSequentialFile(final String fileName, int maxIO) public SequentialFile createSequentialFile(final String fileName)
{ {
if (maxIO < 1)
{
// A single threaded IO
maxIO = 1;
}
return new NIOSequentialFile(this, journalDir, fileName, maxIO, writeExecutor); return new NIOSequentialFile(this, journalDir, fileName, maxIO, writeExecutor);
} }

View File

@ -16,7 +16,9 @@
*/ */
package org.apache.activemq.artemis.core.journal; package org.apache.activemq.artemis.core.journal;
public interface IOCompletion extends IOAsyncTask import org.apache.activemq.artemis.core.io.IOCallback;
public interface IOCompletion extends IOCallback
{ {
void storeLineUp(); void storeLineUp();
} }

View File

@ -19,6 +19,7 @@ package org.apache.activemq.artemis.core.journal;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.JournalFile; import org.apache.activemq.artemis.core.journal.impl.JournalFile;
import org.apache.activemq.artemis.core.server.ActiveMQComponent; import org.apache.activemq.artemis.core.server.ActiveMQComponent;

View File

@ -24,8 +24,8 @@ import java.util.Set;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding; import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalInternalRecord; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalInternalRecord;
@ -87,7 +87,7 @@ public abstract class AbstractJournalUpdateTask implements JournalReaderCallback
final List<Pair<String, String>> renames) throws Exception final List<Pair<String, String>> renames) throws Exception
{ {
SequentialFile controlFile = fileFactory.createSequentialFile(AbstractJournalUpdateTask.FILE_COMPACT_CONTROL, 1); SequentialFile controlFile = fileFactory.createSequentialFile(AbstractJournalUpdateTask.FILE_COMPACT_CONTROL);
try try
{ {
@ -182,7 +182,7 @@ public abstract class AbstractJournalUpdateTask implements JournalReaderCallback
// To Fix the size of the file // To Fix the size of the file
writingChannel.writerIndex(writingChannel.capacity()); writingChannel.writerIndex(writingChannel.capacity());
sequentialFile.writeInternal(writingChannel.toByteBuffer()); sequentialFile.writeDirect(writingChannel.toByteBuffer(), true);
sequentialFile.close(); sequentialFile.close();
newDataFiles.add(currentFile); newDataFiles.add(currentFile);
} }

View File

@ -32,7 +32,7 @@ import org.apache.activemq.artemis.core.journal.JournalLoadInformation;
import org.apache.activemq.artemis.core.journal.LoaderCallback; import org.apache.activemq.artemis.core.journal.LoaderCallback;
import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.TransactionFailureCallback; import org.apache.activemq.artemis.core.journal.TransactionFailureCallback;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecordTX; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecordTX;

View File

@ -17,6 +17,7 @@
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.journal.impl;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.core.io.DummyCallback;
import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.core.journal.EncodingSupport;
import org.apache.activemq.artemis.core.journal.IOCompletion; import org.apache.activemq.artemis.core.journal.IOCompletion;
import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.Journal;

View File

@ -28,8 +28,8 @@ import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding; import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecordTX; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecordTX;
@ -64,7 +64,7 @@ public class JournalCompactor extends AbstractJournalUpdateTask implements Journ
final List<String> newFiles, final List<String> newFiles,
final List<Pair<String, String>> renameFile) throws Exception final List<Pair<String, String>> renameFile) throws Exception
{ {
SequentialFile controlFile = fileFactory.createSequentialFile(AbstractJournalUpdateTask.FILE_COMPACT_CONTROL, 1); SequentialFile controlFile = fileFactory.createSequentialFile(AbstractJournalUpdateTask.FILE_COMPACT_CONTROL);
if (controlFile.exists()) if (controlFile.exists())
{ {

View File

@ -16,7 +16,7 @@
*/ */
package org.apache.activemq.artemis.core.journal.impl; package org.apache.activemq.artemis.core.journal.impl;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
public interface JournalFile public interface JournalFile
{ {

View File

@ -21,7 +21,7 @@ import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
public class JournalFileImpl implements JournalFile public class JournalFileImpl implements JournalFile
{ {

View File

@ -31,8 +31,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger; import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
/** /**
@ -662,13 +662,13 @@ public class JournalFilesRepository
String tmpFileName = fileName + ".tmp"; String tmpFileName = fileName + ".tmp";
SequentialFile sequentialFile = fileFactory.createSequentialFile(tmpFileName, maxAIO); SequentialFile sequentialFile = fileFactory.createSequentialFile(tmpFileName);
sequentialFile.open(1, false); sequentialFile.open(1, false);
if (init) if (init)
{ {
sequentialFile.fill(0, fileSize, JournalImpl.FILL_CHARACTER); sequentialFile.fill(fileSize);
JournalImpl.initFileHeader(fileFactory, sequentialFile, userVersion, fileID); JournalImpl.initFileHeader(fileFactory, sequentialFile, userVersion, fileID);
} }

View File

@ -46,15 +46,15 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.core.journal.EncodingSupport;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.journal.IOCompletion; import org.apache.activemq.artemis.core.journal.IOCompletion;
import org.apache.activemq.artemis.core.journal.JournalLoadInformation; import org.apache.activemq.artemis.core.journal.JournalLoadInformation;
import org.apache.activemq.artemis.core.journal.LoaderCallback; import org.apache.activemq.artemis.core.journal.LoaderCallback;
import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.TestableJournal; import org.apache.activemq.artemis.core.journal.TestableJournal;
import org.apache.activemq.artemis.core.journal.TransactionFailureCallback; import org.apache.activemq.artemis.core.journal.TransactionFailureCallback;
import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding; import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding;
@ -293,7 +293,7 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
final CountDownLatch latch = new CountDownLatch(numIts * 2); final CountDownLatch latch = new CountDownLatch(numIts * 2);
class MyIOAsyncTask implements IOCompletion class MyAIOCallback implements IOCompletion
{ {
public void done() public void done()
{ {
@ -310,7 +310,7 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
} }
} }
final MyIOAsyncTask task = new MyIOAsyncTask(); final MyAIOCallback task = new MyAIOCallback();
final int recordSize = 1024; final int recordSize = 1024;
@ -373,11 +373,11 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
for (String fileName : fileNames) for (String fileName : fileNames)
{ {
SequentialFile file = fileFactory.createSequentialFile(fileName, filesRepository.getMaxAIO()); SequentialFile file = fileFactory.createSequentialFile(fileName);
if (file.size() >= SIZE_HEADER) if (file.size() >= SIZE_HEADER)
{ {
file.open(1, false); file.open();
try try
{ {
@ -2776,11 +2776,11 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
final boolean completeTransaction, final boolean completeTransaction,
final boolean sync, final boolean sync,
final JournalTransaction tx, final JournalTransaction tx,
final IOAsyncTask parameterCallback) throws Exception final IOCallback parameterCallback) throws Exception
{ {
checkJournalIsLoaded(); checkJournalIsLoaded();
final IOAsyncTask callback; final IOCallback callback;
final int size = encoder.getEncodeSize(); final int size = encoder.getEncodeSize();
@ -2896,7 +2896,7 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
{ {
for (String dataFile : dataFiles) for (String dataFile : dataFiles)
{ {
SequentialFile file = fileFactory.createSequentialFile(dataFile, 1); SequentialFile file = fileFactory.createSequentialFile(dataFile);
if (file.exists()) if (file.exists())
{ {
file.delete(); file.delete();
@ -2905,7 +2905,7 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
for (String newFile : newFiles) for (String newFile : newFiles)
{ {
SequentialFile file = fileFactory.createSequentialFile(newFile, 1); SequentialFile file = fileFactory.createSequentialFile(newFile);
if (file.exists()) if (file.exists())
{ {
final String originalName = file.getFileName(); final String originalName = file.getFileName();
@ -2916,8 +2916,8 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
for (Pair<String, String> rename : renames) for (Pair<String, String> rename : renames)
{ {
SequentialFile fileTmp = fileFactory.createSequentialFile(rename.getA(), 1); SequentialFile fileTmp = fileFactory.createSequentialFile(rename.getA());
SequentialFile fileTo = fileFactory.createSequentialFile(rename.getB(), 1); SequentialFile fileTo = fileFactory.createSequentialFile(rename.getB());
// We should do the rename only if the tmp file still exist, or else we could // We should do the rename only if the tmp file still exist, or else we could
// delete a valid file depending on where the crash occurred during the control file delete // delete a valid file depending on where the crash occurred during the control file delete
if (fileTmp.exists()) if (fileTmp.exists())
@ -2951,7 +2951,7 @@ public class JournalImpl extends JournalBase implements TestableJournal, Journal
for (String fileToDelete : leftFiles) for (String fileToDelete : leftFiles)
{ {
ActiveMQJournalLogger.LOGGER.deletingOrphanedFile(fileToDelete); ActiveMQJournalLogger.LOGGER.deletingOrphanedFile(fileToDelete);
SequentialFile file = fileFactory.createSequentialFile(fileToDelete, 1); SequentialFile file = fileFactory.createSequentialFile(fileToDelete);
file.delete(); file.delete();
} }
} }

View File

@ -1,354 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.journal.impl;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.journal.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
/**
* A SyncSpeedTest
*
* This class just provides some diagnostics on how fast your disk can sync
* Useful when determining performance issues
*/
public class SyncSpeedTest
{
public static void main(final String[] args)
{
try
{
new SyncSpeedTest().testScaleAIO();
}
catch (Exception e)
{
e.printStackTrace();
}
}
protected SequentialFileFactory fileFactory;
public boolean AIO = true;
protected void setupFactory()
{
if (AIO)
{
fileFactory = new AIOSequentialFileFactory(new File("."), 0, 0, false, null);
}
else
{
fileFactory = new NIOSequentialFileFactory(new File("."), false, 0, 0, false, null);
}
}
protected SequentialFile createSequentialFile(final String fileName)
{
if (AIO)
{
return new AIOSequentialFile(fileFactory,
0,
0,
new File("."),
fileName,
100000,
null,
null,
Executors.newSingleThreadExecutor());
}
else
{
return new NIOSequentialFile(fileFactory, new File("."), fileName, 1000, null);
}
}
public void run2() throws Exception
{
setupFactory();
int recordSize = 128 * 1024;
while (true)
{
System.out.println("** record size is " + recordSize);
int warmup = 500;
int its = 500;
int fileSize = (its + warmup) * recordSize;
SequentialFile file = createSequentialFile("sync-speed-test.dat");
if (file.exists())
{
file.delete();
}
file.open();
file.fill(0, fileSize, (byte)'X');
if (!AIO)
{
file.sync();
}
ByteBuffer bb1 = generateBuffer(recordSize, (byte)'h');
long start = 0;
for (int i = 0; i < its + warmup; i++)
{
if (i == warmup)
{
start = System.currentTimeMillis();
}
bb1.rewind();
file.writeDirect(bb1, true);
}
long end = System.currentTimeMillis();
double rate = 1000 * (double)its / (end - start);
double throughput = recordSize * rate;
System.out.println("Rate of " + rate + " syncs per sec");
System.out.println("Throughput " + throughput + " bytes per sec");
System.out.println("*************");
recordSize *= 2;
}
}
public void run() throws Exception
{
int recordSize = 256;
while (true)
{
System.out.println("** record size is " + recordSize);
int warmup = 500;
int its = 500;
int fileSize = (its + warmup) * recordSize;
File file = new File("sync-speed-test.dat");
if (file.exists())
{
if (!file.delete())
{
ActiveMQJournalLogger.LOGGER.errorDeletingFile(file);
}
}
boolean created = file.createNewFile();
if (!created)
throw new IOException("could not create file " + file);
RandomAccessFile rfile = new RandomAccessFile(file, "rw");
FileChannel channel = rfile.getChannel();
ByteBuffer bb = generateBuffer(fileSize, (byte)'x');
write(bb, channel, fileSize);
channel.force(true);
channel.position(0);
ByteBuffer bb1 = generateBuffer(recordSize, (byte)'h');
long start = 0;
for (int i = 0; i < its + warmup; i++)
{
if (i == warmup)
{
start = System.currentTimeMillis();
}
bb1.flip();
channel.write(bb1);
channel.force(false);
}
long end = System.currentTimeMillis();
double rate = 1000 * (double)its / (end - start);
double throughput = recordSize * rate;
System.out.println("Rate of " + rate + " syncs per sec");
System.out.println("Throughput " + throughput + " bytes per sec");
recordSize *= 2;
}
}
public void testScaleAIO() throws Exception
{
setupFactory();
final int recordSize = 1024;
System.out.println("** record size is " + recordSize);
final int its = 10;
for (int numThreads = 1; numThreads <= 10; numThreads++)
{
int fileSize = its * recordSize * numThreads;
final SequentialFile file = createSequentialFile("sync-speed-test.dat");
if (file.exists())
{
file.delete();
}
file.open();
file.fill(0, fileSize, (byte)'X');
if (!AIO)
{
file.sync();
}
final CountDownLatch latch = new CountDownLatch(its * numThreads);
class MyIOAsyncTask implements IOAsyncTask
{
public void done()
{
latch.countDown();
}
public void onError(final int errorCode, final String errorMessage)
{
}
}
final MyIOAsyncTask task = new MyIOAsyncTask();
class MyRunner implements Runnable
{
private final ByteBuffer bb1;
MyRunner()
{
bb1 = generateBuffer(recordSize, (byte)'h');
}
public void run()
{
for (int i = 0; i < its; i++)
{
bb1.rewind();
file.writeDirect(bb1, true, task);
// try
// {
// file.writeDirect(bb1, true);
// }
// catch (Exception e)
// {
// e.printStackTrace();
// }
}
}
}
Set<Thread> threads = new HashSet<Thread>();
for (int i = 0; i < numThreads; i++)
{
MyRunner runner = new MyRunner();
Thread t = new Thread(runner);
threads.add(t);
}
long start = System.currentTimeMillis();
for (Thread t : threads)
{
ActiveMQJournalLogger.LOGGER.startingThread();
t.start();
}
for (Thread t : threads)
{
t.join();
}
latch.await();
long end = System.currentTimeMillis();
double rate = 1000 * (double)its * numThreads / (end - start);
double throughput = recordSize * rate;
System.out.println("For " + numThreads + " threads:");
System.out.println("Rate of " + rate + " records per sec");
System.out.println("Throughput " + throughput + " bytes per sec");
System.out.println("*************");
}
}
private void write(final ByteBuffer buffer, final FileChannel channel, final int size) throws Exception
{
buffer.flip();
channel.write(buffer);
}
private ByteBuffer generateBuffer(final int size, final byte ch)
{
ByteBuffer bb = ByteBuffer.allocateDirect(size);
for (int i = 0; i < size; i++)
{
bb.put(ch);
}
return bb;
}
}

View File

@ -18,10 +18,10 @@ package org.apache.activemq.artemis.core.journal.impl;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.utils.ReusableLatch; import org.apache.activemq.artemis.utils.ReusableLatch;
public class TransactionCallback implements IOAsyncTask public class TransactionCallback implements IOCallback
{ {
private final ReusableLatch countLatch = new ReusableLatch(); private final ReusableLatch countLatch = new ReusableLatch();
@ -33,7 +33,7 @@ public class TransactionCallback implements IOAsyncTask
private int done = 0; private int done = 0;
private volatile IOAsyncTask delegateCompletion; private volatile IOCallback delegateCompletion;
public void countUp() public void countUp()
{ {
@ -46,7 +46,7 @@ public class TransactionCallback implements IOAsyncTask
countLatch.countDown(); countLatch.countDown();
if (++done == up.get() && delegateCompletion != null) if (++done == up.get() && delegateCompletion != null)
{ {
final IOAsyncTask delegateToCall = delegateCompletion; final IOCallback delegateToCall = delegateCompletion;
// We need to set the delegateCompletion to null first or blocking commits could miss a callback // We need to set the delegateCompletion to null first or blocking commits could miss a callback
// What would affect mainly tests // What would affect mainly tests
delegateCompletion = null; delegateCompletion = null;
@ -81,7 +81,7 @@ public class TransactionCallback implements IOAsyncTask
/** /**
* @return the delegateCompletion * @return the delegateCompletion
*/ */
public IOAsyncTask getDelegateCompletion() public IOCallback getDelegateCompletion()
{ {
return delegateCompletion; return delegateCompletion;
} }
@ -89,7 +89,7 @@ public class TransactionCallback implements IOAsyncTask
/** /**
* @param delegateCompletion the delegateCompletion to set * @param delegateCompletion the delegateCompletion to set
*/ */
public void setDelegateCompletion(final IOAsyncTask delegateCompletion) public void setDelegateCompletion(final IOCallback delegateCompletion)
{ {
this.delegateCompletion = delegateCompletion; this.delegateCompletion = delegateCompletion;
} }

View File

@ -0,0 +1,97 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.io.aio;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
/** This will emulate callbacks out of order from libaio*/
public class CallbackOrderTest
{
@Rule
public TemporaryFolder temporaryFolder;
public CallbackOrderTest()
{
File parent = new File("./target");
parent.mkdirs();
temporaryFolder = new TemporaryFolder(parent);
}
/** This method will make sure callbacks will come back in order even when out order from libaio */
@Test
public void testCallbackOutOfOrder() throws Exception
{
AIOSequentialFileFactory factory = new AIOSequentialFileFactory(temporaryFolder.getRoot(), 100);
AIOSequentialFile file = (AIOSequentialFile)factory.createSequentialFile("test.bin");
final AtomicInteger count = new AtomicInteger(0);
IOCallback callback = new IOCallback()
{
@Override
public void done()
{
count.incrementAndGet();
}
@Override
public void onError(int errorCode, String errorMessage)
{
}
};
ArrayList<AIOSequentialFileFactory.AIOSequentialCallback> list = new ArrayList<>();
// We will repeat the teset a few times, increasing N
// to increase possibility of issues due to reuse of callbacks
for (int n = 1; n < 100; n++)
{
System.out.println("n = " + n);
int N = n;
count.set(0);
list.clear();
for (int i = 0; i < N; i++)
{
list.add(file.getCallback(callback, null));
}
for (int i = N - 1; i >= 0; i--)
{
list.get(i).done();
}
Assert.assertEquals(N, count.get());
Assert.assertEquals(0, file.pendingCallbackList.size());
Assert.assertTrue(file.pendingCallbackList.isEmpty());
}
factory.stop();
}
}

View File

@ -250,6 +250,7 @@ public class ActiveMQCreatePlugin extends AbstractMojo
add(listCommands, "--failover-on-shutdown"); add(listCommands, "--failover-on-shutdown");
} }
add(listCommands, "--no-sync-test");
add(listCommands, "--verbose"); add(listCommands, "--verbose");
add(listCommands, instance.getAbsolutePath()); add(listCommands, instance.getAbsolutePath());

View File

@ -32,6 +32,30 @@
<artifactId>artemis-commons</artifactId> <artifactId>artemis-commons</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging-processor</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,63 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include "AIOController.h"
#include "JavaUtilities.h"
#include "JAIODatatypes.h"
AIOController::AIOController(std::string fileName, int maxIO) : logger(0), fileOutput(fileName, this, maxIO)
{
}
void AIOController::log(THREAD_CONTEXT threadContext, short level, const char * message)
{
jmethodID methodID = 0;
switch (level)
{
case 0: methodID = loggerError; break;
case 1: methodID = loggerWarn; break;
case 2: methodID = loggerInfo; break;
case 3: methodID = loggerDebug; break;
default: methodID = loggerDebug; break;
}
#ifdef DEBUG
fprintf (stderr,"Callig log methodID=%ld, message=%s, logger=%ld, threadContext = %ld\n", (long) methodID, message, (long) logger, (long) threadContext); fflush(stderr);
#endif
threadContext->CallVoidMethod(logger,methodID,threadContext->NewStringUTF(message));
}
void AIOController::destroy(THREAD_CONTEXT context)
{
if (logger != 0)
{
context->DeleteGlobalRef(logger);
}
}
/*
* level = 0-error, 1-warn, 2-info, 3-debug
*/
AIOController::~AIOController()
{
}

View File

@ -1,51 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef AIOCONTROLLER_H_
#define AIOCONTROLLER_H_
#include <jni.h>
#include <string>
#include "JAIODatatypes.h"
#include "AsyncFile.h"
class AIOController
{
public:
jmethodID done;
jmethodID error;
jobject logger;
jmethodID loggerError;
jmethodID loggerWarn;
jmethodID loggerDebug;
jmethodID loggerInfo;
/*
* level = 0-error, 1-warn, 2-info, 3-debug
*/
void log(THREAD_CONTEXT threadContext, short level, const char * message);
AsyncFile fileOutput;
void destroy(THREAD_CONTEXT context);
AIOController(std::string fileName, int maxIO);
virtual ~AIOController();
};
#endif /*AIOCONTROLLER_H_*/

View File

@ -1,75 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef AIOEXCEPTION_H_
#define AIOEXCEPTION_H_
#include <exception>
#include <string>
#define NATIVE_ERROR_INTERNAL 200
#define NATIVE_ERROR_INVALID_BUFFER 201
#define NATIVE_ERROR_NOT_ALIGNED 202
#define NATIVE_ERROR_CANT_INITIALIZE_AIO 203
#define NATIVE_ERROR_CANT_RELEASE_AIO 204
#define NATIVE_ERROR_CANT_OPEN_CLOSE_FILE 205
#define NATIVE_ERROR_CANT_ALLOCATE_QUEUE 206
#define NATIVE_ERROR_PREALLOCATE_FILE 208
#define NATIVE_ERROR_ALLOCATE_MEMORY 209
#define NATIVE_ERROR_IO 006
#define NATIVE_ERROR_AIO_FULL 211
class AIOException : public std::exception
{
private:
int errorCode;
std::string message;
public:
AIOException(int _errorCode, std::string _message) throw() : errorCode(_errorCode), message(_message)
{
errorCode = _errorCode;
message = _message;
}
AIOException(int _errorCode, const char * _message) throw ()
{
message = std::string(_message);
errorCode = _errorCode;
}
virtual ~AIOException() throw()
{
}
int inline getErrorCode()
{
return errorCode;
}
const char* what() const throw()
{
return message.data();
}
};
#endif /*AIOEXCEPTION_H_*/

View File

@ -1,348 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <list>
#include <iostream>
#include <sstream>
#include <memory.h>
#include <errno.h>
#include <libaio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "AsyncFile.h"
#include "AIOController.h"
#include "AIOException.h"
#include "pthread.h"
#include "LockClass.h"
#include "CallbackAdapter.h"
#include "LockClass.h"
//#define DEBUG
#define WAIT_FOR_SPOT 10000
#define TRIES_BEFORE_WARN 0
#define TRIES_BEFORE_ERROR 500
std::string io_error(int rc)
{
std::stringstream buffer;
if (rc == -ENOSYS)
buffer << "AIO not in this kernel";
else
buffer << "Error:= " << strerror((int)-rc);
return buffer.str();
}
AsyncFile::AsyncFile(std::string & _fileName, AIOController * _controller, int _maxIO) : aioContext(0), events(0), fileHandle(0), controller(_controller), pollerRunning(0)
{
::pthread_mutex_init(&fileMutex,0);
::pthread_mutex_init(&pollerMutex,0);
maxIO = _maxIO;
fileName = _fileName;
if (io_queue_init(maxIO, &aioContext))
{
throw AIOException(NATIVE_ERROR_CANT_INITIALIZE_AIO, "Can't initialize aio, out of AIO Handlers");
}
fileHandle = ::open(fileName.data(), O_RDWR | O_CREAT | O_DIRECT, 0666);
if (fileHandle < 0)
{
io_queue_release(aioContext);
throw AIOException(NATIVE_ERROR_CANT_OPEN_CLOSE_FILE, "Can't open file");
}
#ifdef DEBUG
fprintf (stderr,"File Handle %d", fileHandle);
#endif
events = (struct io_event *)malloc (maxIO * sizeof (struct io_event));
if (events == 0)
{
throw AIOException (NATIVE_ERROR_CANT_ALLOCATE_QUEUE, "Can't allocate ioEvents");
}
}
AsyncFile::~AsyncFile()
{
if (io_queue_release(aioContext))
{
throw AIOException(NATIVE_ERROR_CANT_RELEASE_AIO,"Can't release aio");
}
if (::close(fileHandle))
{
throw AIOException(NATIVE_ERROR_CANT_OPEN_CLOSE_FILE,"Can't close file");
}
free(events);
::pthread_mutex_destroy(&fileMutex);
::pthread_mutex_destroy(&pollerMutex);
}
int isException (THREAD_CONTEXT threadContext)
{
return JNI_ENV(threadContext)->ExceptionOccurred() != 0;
}
void AsyncFile::pollEvents(THREAD_CONTEXT threadContext)
{
LockClass lock(&pollerMutex);
pollerRunning=1;
while (pollerRunning)
{
if (isException(threadContext))
{
return;
}
int result = io_getevents(this->aioContext, 1, maxIO, events, 0);
#ifdef DEBUG
fprintf (stderr, "poll, pollerRunning=%d\n", pollerRunning); fflush(stderr);
#endif
if (result > 0)
{
#ifdef DEBUG
fprintf (stdout, "Received %d events\n", result);
fflush(stdout);
#endif
}
for (int i=0; i<result; i++)
{
struct iocb * iocbp = events[i].obj;
if (iocbp->data == (void *) -1)
{
pollerRunning = 0;
#ifdef DEBUG
controller->log(threadContext, 2, "Received poller request to stop");
#endif
}
else
{
CallbackAdapter * adapter = (CallbackAdapter *) iocbp->data;
long result = events[i].res;
if (result < 0)
{
std::string strerror = io_error((int)result);
adapter->onError(threadContext, result, strerror);
}
else
{
adapter->done(threadContext);
}
}
delete iocbp;
}
}
#ifdef DEBUG
controller->log(threadContext, 2, "Poller finished execution");
#endif
}
void AsyncFile::preAllocate(THREAD_CONTEXT , off_t position, int blocks, size_t size, int fillChar)
{
if (size % ALIGNMENT != 0)
{
throw AIOException (NATIVE_ERROR_PREALLOCATE_FILE, "You can only pre allocate files in multiples of 512");
}
void * preAllocBuffer = 0;
if (posix_memalign(&preAllocBuffer, 512, size))
{
throw AIOException(NATIVE_ERROR_ALLOCATE_MEMORY, "Error on posix_memalign");
}
memset(preAllocBuffer, fillChar, size);
if (::lseek (fileHandle, position, SEEK_SET) < 0) throw AIOException (11, "Error positioning the file");
for (int i=0; i<blocks; i++)
{
if (::write(fileHandle, preAllocBuffer, size)<0)
{
throw AIOException (NATIVE_ERROR_PREALLOCATE_FILE, "Error pre allocating the file");
}
}
if (::lseek (fileHandle, position, SEEK_SET) < 0) throw AIOException (NATIVE_ERROR_IO, "Error positioning the file");
free (preAllocBuffer);
}
/** Write directly to the file without using libaio queue */
void AsyncFile::writeInternal(THREAD_CONTEXT, long position, size_t size, void *& buffer)
{
if (::lseek (fileHandle, position, SEEK_SET) < 0) throw AIOException (11, "Error positioning the file");
if (::write(fileHandle, buffer, size)<0)
{
throw AIOException (NATIVE_ERROR_IO, "Error writing file");
}
if (::fsync(fileHandle) < 0)
{
throw AIOException (NATIVE_ERROR_IO, "Error on synchronizing file");
}
}
void AsyncFile::write(THREAD_CONTEXT threadContext, long position, size_t size, void *& buffer, CallbackAdapter *& adapter)
{
struct iocb * iocb = new struct iocb();
::io_prep_pwrite(iocb, fileHandle, buffer, size, position);
iocb->data = (void *) adapter;
int tries = 0;
int result = 0;
while ((result = ::io_submit(aioContext, 1, &iocb)) == (-EAGAIN))
{
#ifdef DEBUG
fprintf (stderr, "Retrying block as iocb was full (retry=%d)\n", tries);
#endif
tries ++;
if (tries > TRIES_BEFORE_WARN)
{
#ifdef DEBUG
fprintf (stderr, "Warning level on retries, informing logger (retry=%d)\n", tries);
#endif
controller->log(threadContext, 1, "You should consider expanding AIOLimit if this message appears too many times");
}
if (tries > TRIES_BEFORE_ERROR)
{
#ifdef DEBUG
fprintf (stderr, "Error level on retries, throwing exception (retry=%d)\n", tries);
#endif
throw AIOException(NATIVE_ERROR_AIO_FULL, "Too many retries (500) waiting for a valid iocb block, please increase MAX_IO limit");
}
::usleep(WAIT_FOR_SPOT);
}
if (result<0)
{
std::stringstream str;
str<< "Problem on submit block, errorCode=" << result;
throw AIOException (NATIVE_ERROR_IO, str.str());
}
}
void AsyncFile::read(THREAD_CONTEXT threadContext, long position, size_t size, void *& buffer, CallbackAdapter *& adapter)
{
struct iocb * iocb = new struct iocb();
::io_prep_pread(iocb, fileHandle, buffer, size, position);
iocb->data = (void *) adapter;
int tries = 0;
int result = 0;
while ((result = ::io_submit(aioContext, 1, &iocb)) == (-EAGAIN))
{
#ifdef DEBUG
fprintf (stderr, "Retrying block as iocb was full (retry=%d)\n", tries);
#endif
tries ++;
if (tries > TRIES_BEFORE_WARN)
{
#ifdef DEBUG
fprintf (stderr, "Warning level on retries, informing logger (retry=%d)\n", tries);
#endif
controller->log(threadContext, 1, "You should consider expanding AIOLimit if this message appears too many times");
}
if (tries > TRIES_BEFORE_ERROR)
{
#ifdef DEBUG
fprintf (stderr, "Error level on retries, throwing exception (retry=%d)\n", tries);
#endif
throw AIOException(NATIVE_ERROR_AIO_FULL, "Too many retries (500) waiting for a valid iocb block, please increase MAX_IO limit");
}
::usleep(WAIT_FOR_SPOT);
}
if (result<0)
{
std::stringstream str;
str<< "Problem on submit block, errorCode=" << result;
throw AIOException (NATIVE_ERROR_IO, str.str());
}
}
long AsyncFile::getSize()
{
struct stat statBuffer;
if (fstat(fileHandle, &statBuffer) < 0)
{
return -1l;
}
return statBuffer.st_size;
}
void AsyncFile::stopPoller(THREAD_CONTEXT threadContext)
{
pollerRunning = 0;
struct iocb * iocb = new struct iocb();
::io_prep_pwrite(iocb, fileHandle, 0, 0, 0);
iocb->data = (void *) -1;
int result = 0;
while ((result = ::io_submit(aioContext, 1, &iocb)) == (-EAGAIN))
{
fprintf(stderr, "Couldn't send request to stop poller, trying again");
controller->log(threadContext, 1, "Couldn't send request to stop poller, trying again");
::usleep(WAIT_FOR_SPOT);
}
// Waiting the Poller to finish (by giving up the lock)
LockClass lock(&pollerMutex);
}

View File

@ -1,93 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FILEOUTPUT_H_
#define FILEOUTPUT_H_
#include <string>
#include <libaio.h>
#include <stdlib.h>
#include <pthread.h>
#include "JAIODatatypes.h"
#include "AIOException.h"
class AIOController;
class CallbackAdapter;
/** Author: Clebert Suconic at Redhat dot com*/
class AsyncFile
{
private:
io_context_t aioContext;
struct io_event *events;
int fileHandle;
std::string fileName;
pthread_mutex_t fileMutex;
pthread_mutex_t pollerMutex;
AIOController * controller;
bool pollerRunning;
int maxIO;
public:
AsyncFile(std::string & _fileName, AIOController * controller, int maxIO);
virtual ~AsyncFile();
void write(THREAD_CONTEXT threadContext, long position, size_t size, void *& buffer, CallbackAdapter *& adapter);
/** Write directly to the file without using libaio queue */
void writeInternal(THREAD_CONTEXT threadContext, long position, size_t size, void *& buffer);
void read(THREAD_CONTEXT threadContext, long position, size_t size, void *& buffer, CallbackAdapter *& adapter);
int getHandle()
{
return fileHandle;
}
long getSize();
inline void * newBuffer(int size)
{
void * buffer = 0;
if (::posix_memalign(&buffer, 512, size))
{
throw AIOException(NATIVE_ERROR_ALLOCATE_MEMORY, "Error on posix_memalign");
}
return buffer;
}
inline void destroyBuffer(void * buffer)
{
::free(buffer);
}
// Finishes the polling thread (if any) and return
void stopPoller(THREAD_CONTEXT threadContext);
void preAllocate(THREAD_CONTEXT threadContext, off_t position, int blocks, size_t size, int fillChar);
void pollEvents(THREAD_CONTEXT threadContext);
};
#endif /*FILEOUTPUT_H_*/

View File

@ -30,35 +30,21 @@ endif()
# you may want to remove this next line for debugging # you may want to remove this next line for debugging
# -O3 would make inline debug hard # -O3 would make inline debug hard
#ADD_DEFINITIONS("-O3 -Wall -z execstack")
ADD_DEFINITIONS("-O3 -Wall") ADD_DEFINITIONS("-O3 -Wall")
#ADD_DEFINITIONS("-fdump-tree-all -Wall -pg -g -mstack-protector-guard=guard") #ADD_DEFINITIONS("-fdump-tree-all -Wall -pg -g")
find_library(LIBAIO NAMES aio) find_library(LIBAIO NAMES aio)
INCLUDE_DIRECTORIES(. ${JNI_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(. ${JNI_INCLUDE_DIRS})
ADD_CUSTOM_COMMAND( ADD_CUSTOM_COMMAND(
OUTPUT org_apache_activemq_artemis_core_libaio_Native.h OUTPUT org_apache_activemq_artemis_jlibaio_LibaioContext.h
COMMAND javah -cp ../java/ org.apache.activemq.artemis.core.libaio.Native COMMAND javah -cp ../java/ org.apache.activemq.artemis.jlibaio.LibaioContext
DEPENDS ../java/org/apache/activemq/artemis/core/libaio/Native.java DEPENDS ../java/org/apache/activemq/artemis/jlibaio/LibaioContext.java
) )
ADD_LIBRARY(artemis-native SHARED ADD_LIBRARY(artemis-native SHARED org_apache_activemq_artemis_jlibaio_LibaioContext.c org_apache_activemq_artemis_jlibaio_LibaioContext.h exception_helper.h)
AIOController.cpp
AIOController.h
AIOException.h
AsyncFile.cpp
AsyncFile.h
CallbackAdapter.h
JAIODatatypes.h
JavaUtilities.cpp
JavaUtilities.h
JNI_AsynchronousFileImpl.cpp
JNICallbackAdapter.cpp
JNICallbackAdapter.h
LockClass.h
Version.h
org_apache_activemq_artemis_core_libaio_Native.h)
target_link_libraries(artemis-native aio) target_link_libraries(artemis-native aio)

View File

@ -1,42 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BUFFERADAPTER_H_
#define BUFFERADAPTER_H_
#include <iostream>
#include "JAIODatatypes.h"
class CallbackAdapter
{
private:
public:
CallbackAdapter()
{
}
virtual ~CallbackAdapter()
{
}
virtual void done(THREAD_CONTEXT ) = 0;
virtual void onError(THREAD_CONTEXT , long , std::string )=0;
};
#endif /*BUFFERADAPTER_H_*/

View File

@ -1,28 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef JAIODATATYPES_H_
#define JAIODATATYPES_H_
#include <jni.h>
#define THREAD_CONTEXT JNIEnv *&
#define JNI_ENV(pointer) pointer
#define ALIGNMENT 512
#endif /*JAIODATATYPES_H_*/

View File

@ -1,62 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include "JNICallbackAdapter.h"
#include <iostream>
#include "JavaUtilities.h"
jobject nullObj = NULL;
JNICallbackAdapter::JNICallbackAdapter(AIOController * _controller, jlong _sequence, jobject _callback, jobject _fileController, jobject _bufferReference, short _isRead) : CallbackAdapter()
{
controller = _controller;
sequence = _sequence;
callback = _callback;
fileController = _fileController;
bufferReference = _bufferReference;
isRead = _isRead;
}
JNICallbackAdapter::~JNICallbackAdapter()
{
}
void JNICallbackAdapter::done(THREAD_CONTEXT threadContext)
{
JNI_ENV(threadContext)->CallVoidMethod(fileController, controller->done, callback, sequence, isRead ? nullObj : bufferReference);
release(threadContext);
}
void JNICallbackAdapter::onError(THREAD_CONTEXT threadContext, long errorCode, std::string error)
{
controller->log(threadContext, 0, "Libaio event generated errors, callback object was informed about it");
jstring strError = JNI_ENV(threadContext)->NewStringUTF(error.data());
JNI_ENV(threadContext)->CallVoidMethod(fileController, controller->error, callback, sequence, isRead ? nullObj : bufferReference, (jint)errorCode, strError);
release(threadContext);
}

View File

@ -1,66 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef JNIBUFFERADAPTER_H_
#define JNIBUFFERADAPTER_H_
#include <iostream>
#include "CallbackAdapter.h"
#include "AIOController.h"
#include "JAIODatatypes.h"
class JNICallbackAdapter : public CallbackAdapter
{
private:
AIOController * controller;
jobject callback;
jobject fileController;
jobject bufferReference;
jlong sequence;
// Is this a read operation
short isRead;
void release(THREAD_CONTEXT threadContext)
{
JNI_ENV(threadContext)->DeleteGlobalRef(callback);
JNI_ENV(threadContext)->DeleteGlobalRef(fileController);
JNI_ENV(threadContext)->DeleteGlobalRef(bufferReference);
delete this;
return;
}
public:
// _ob must be a global Reference (use createGloblReferente before calling the constructor)
JNICallbackAdapter(AIOController * _controller, jlong sequence, jobject _callback, jobject _fileController, jobject _bufferReference, short _isRead);
virtual ~JNICallbackAdapter();
void done(THREAD_CONTEXT threadContext);
void onError(THREAD_CONTEXT , long , std::string );
};
#endif /*JNIBUFFERADAPTER_H_*/

View File

@ -1,377 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>
#include <time.h>
#include <sys/file.h>
#include "org_apache_activemq_artemis_core_libaio_Native.h"
#include "JavaUtilities.h"
#include "AIOController.h"
#include "JNICallbackAdapter.h"
#include "AIOException.h"
#include "Version.h"
// This value is set here globally, to avoid passing stuff on stack between java and the native layer on every sleep call
struct timespec nanoTime;
inline AIOController * getController(JNIEnv *env, jobject & controllerAddress)
{
return (AIOController *) env->GetDirectBufferAddress(controllerAddress);
}
/* Inaccessible static: log */
/* Inaccessible static: totalMaxIO */
/* Inaccessible static: loaded */
/* Inaccessible static: EXPECTED_NATIVE_VERSION */
/*
* Class: org.apache.activemq.artemis_core_asyncio_impl_AsynchronousFileImpl
* Method: openFile
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_openFile
(JNIEnv * env , jclass , jstring jstrFileName)
{
std::string fileName = convertJavaString(env, jstrFileName);
return open(fileName.data(), O_RDWR | O_CREAT, 0666);
}
/*
* Class: org.apache.activemq.artemis_core_asyncio_impl_AsynchronousFileImpl
* Method: closeFile
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_closeFile
(JNIEnv * , jclass , jint handle)
{
close(handle);
}
/*
* Class: org.apache.activemq.artemis_core_asyncio_impl_AsynchronousFileImpl
* Method: flock
* Signature: (I)Z
*/
JNIEXPORT jboolean JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_flock
(JNIEnv * , jclass , jint handle)
{
return flock(handle, LOCK_EX | LOCK_NB) == 0;
}
/*
* Class: org_jboss_jaio_libaioimpl_LibAIOController
* Method: init
* Signature: (Ljava/lang/String;Ljava/lang/Class;)J
*/
JNIEXPORT jobject JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_init
(JNIEnv * env, jclass, jclass controllerClazz, jstring jstrFileName, jint maxIO, jobject logger)
{
AIOController * controller = 0;
try
{
std::string fileName = convertJavaString(env, jstrFileName);
controller = new AIOController(fileName, (int) maxIO);
controller->done = env->GetMethodID(controllerClazz,"callbackDone","(Lorg/apache/activemq/artemis/core/asyncio/AIOCallback;JLjava/nio/ByteBuffer;)V");
if (!controller->done)
{
throwException (env, -1, "can't get callbackDone method");
return 0;
}
controller->error = env->GetMethodID(controllerClazz, "callbackError", "(Lorg/apache/activemq/artemis/core/asyncio/AIOCallback;JLjava/nio/ByteBuffer;ILjava/lang/String;)V");
if (!controller->done)
{
throwException (env, -1, "can't get callbackError method");
return 0;
}
jclass loggerClass = env->GetObjectClass(logger);
if (!(controller->loggerDebug = env->GetMethodID(loggerClass, "debug", "(Ljava/lang/Object;)V"))) return 0;
if (!(controller->loggerWarn = env->GetMethodID(loggerClass, "warn", "(Ljava/lang/Object;)V"))) return 0;
if (!(controller->loggerInfo = env->GetMethodID(loggerClass, "info", "(Ljava/lang/Object;)V"))) return 0;
if (!(controller->loggerError = env->GetMethodID(loggerClass, "error", "(Ljava/lang/Object;)V"))) return 0;
controller->logger = env->NewGlobalRef(logger);
return env->NewDirectByteBuffer(controller, 0);
}
catch (AIOException& e){
if (controller != 0)
{
delete controller;
}
throwException(env, e.getErrorCode(), e.what());
return 0;
}
}
/**
* objThis here is passed as a parameter at the java layer. It used to be a JNI this and now it's a java static method
where the intended reference is now passed as an argument
*/
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_read
(JNIEnv *env, jclass, jobject objThis, jobject controllerAddress, jlong position, jlong size, jobject jbuffer, jobject callback)
{
try
{
AIOController * controller = getController(env, controllerAddress);
void * buffer = env->GetDirectBufferAddress(jbuffer);
if (buffer == 0)
{
throwException(env, NATIVE_ERROR_INVALID_BUFFER, "Invalid Buffer used, libaio requires NativeBuffer instead of Java ByteBuffer");
return;
}
if (((long)buffer) % 512)
{
throwException(env, NATIVE_ERROR_NOT_ALIGNED, "Buffer not aligned for use with DMA");
return;
}
CallbackAdapter * adapter = new JNICallbackAdapter(controller, -1, env->NewGlobalRef(callback), env->NewGlobalRef(objThis), env->NewGlobalRef(jbuffer), true);
controller->fileOutput.read(env, position, (size_t)size, buffer, adapter);
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
}
}
// Fast memset on buffer
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_resetBuffer
(JNIEnv *env, jclass, jobject jbuffer, jint size)
{
void * buffer = env->GetDirectBufferAddress(jbuffer);
if (buffer == 0)
{
throwException(env, NATIVE_ERROR_INVALID_BUFFER, "Invalid Buffer used, libaio requires NativeBuffer instead of Java ByteBuffer");
return;
}
memset(buffer, 0, (size_t)size);
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_destroyBuffer
(JNIEnv * env, jclass, jobject jbuffer)
{
if (jbuffer == 0)
{
throwException(env, NATIVE_ERROR_INVALID_BUFFER, "Null Buffer");
return;
}
void * buffer = env->GetDirectBufferAddress(jbuffer);
free(buffer);
}
JNIEXPORT jobject JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_newNativeBuffer
(JNIEnv * env, jclass, jlong size)
{
try
{
if (size % ALIGNMENT)
{
throwException(env, NATIVE_ERROR_INVALID_BUFFER, "Buffer size needs to be aligned to 512");
return 0;
}
// This will allocate a buffer, aligned by 512.
// Buffers created here need to be manually destroyed by destroyBuffer, or this would leak on the process heap away of Java's GC managed memory
void * buffer = 0;
if (::posix_memalign(&buffer, 512, size))
{
throwException(env, NATIVE_ERROR_INTERNAL, "Error on posix_memalign");
return 0;
}
memset(buffer, 0, (size_t)size);
jobject jbuffer = env->NewDirectByteBuffer(buffer, size);
return jbuffer;
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
return 0;
}
}
/**
* objThis here is passed as a parameter at the java layer. It used to be a JNI this and now it's a java static method
where the intended reference is now passed as an argument
*/
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_write
(JNIEnv *env, jclass, jobject objThis, jobject controllerAddress, jlong sequence, jlong position, jlong size, jobject jbuffer, jobject callback)
{
try
{
AIOController * controller = getController(env, controllerAddress);
void * buffer = env->GetDirectBufferAddress(jbuffer);
if (buffer == 0)
{
throwException(env, NATIVE_ERROR_INVALID_BUFFER, "Invalid Buffer used, libaio requires NativeBuffer instead of Java ByteBuffer");
return;
}
CallbackAdapter * adapter = new JNICallbackAdapter(controller, sequence, env->NewGlobalRef(callback), env->NewGlobalRef(objThis), env->NewGlobalRef(jbuffer), false);
controller->fileOutput.write(env, position, (size_t)size, buffer, adapter);
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
}
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_writeInternal
(JNIEnv * env, jclass, jobject controllerAddress, jlong positionToWrite, jlong size, jobject jbuffer)
{
try
{
AIOController * controller = getController(env, controllerAddress);
void * buffer = env->GetDirectBufferAddress(jbuffer);
if (buffer == 0)
{
throwException(env, NATIVE_ERROR_INVALID_BUFFER, "Invalid Buffer used, libaio requires NativeBuffer instead of Java ByteBuffer");
return;
}
controller->fileOutput.writeInternal(env, positionToWrite, (size_t)size, buffer);
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
}
}
JNIEXPORT void Java_org_apache_activemq_artemis_core_libaio_Native_internalPollEvents
(JNIEnv *env, jclass, jobject controllerAddress)
{
try
{
AIOController * controller = getController(env, controllerAddress);
controller->fileOutput.pollEvents(env);
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
}
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_stopPoller
(JNIEnv *env, jclass, jobject controllerAddress)
{
try
{
AIOController * controller = getController(env, controllerAddress);
controller->fileOutput.stopPoller(env);
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
}
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_closeInternal
(JNIEnv *env, jclass, jobject controllerAddress)
{
try
{
AIOController * controller = getController(env, controllerAddress);
controller->destroy(env);
delete controller;
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
}
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_fill
(JNIEnv * env, jclass, jobject controllerAddress, jlong position, jint blocks, jlong size, jbyte fillChar)
{
try
{
AIOController * controller = getController(env, controllerAddress);
controller->fileOutput.preAllocate(env, position, blocks, size, fillChar);
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
}
}
/** It does nothing... just return true to make sure it has all the binary dependencies */
JNIEXPORT jint JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_getNativeVersion
(JNIEnv *, jclass)
{
return _VERSION_NATIVE_AIO;
}
JNIEXPORT jlong JNICALL Java_org_apache_activemq_artemis_core_libaio_Native_size0
(JNIEnv * env, jclass, jobject controllerAddress)
{
try
{
AIOController * controller = getController(env, controllerAddress);
long size = controller->fileOutput.getSize();
if (size < 0)
{
throwException(env, NATIVE_ERROR_INTERNAL, "InternalError on Native Layer: method size failed");
return -1l;
}
return size;
}
catch (AIOException& e)
{
throwException(env, e.getErrorCode(), e.what());
return -1l;
}
}

View File

@ -1,62 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <iostream>
#include <string>
#include "JavaUtilities.h"
void throwRuntimeException(JNIEnv * env, const char * message)
{
jclass exceptionClass = env->FindClass("java/lang/RuntimeException");
env->ThrowNew(exceptionClass,message);
}
void throwException(JNIEnv * env, const int code, const char * message)
{
jclass exceptionClass = env->FindClass("org/apache/activemq/artemis/api/core/ActiveMQException");
if (exceptionClass==NULL)
{
std::cerr << "Couldn't throw exception message:= " << message << "\n";
throwRuntimeException (env, "Can't find Exception class");
return;
}
jmethodID constructor = env->GetMethodID(exceptionClass, "<init>", "(ILjava/lang/String;)V");
if (constructor == NULL)
{
std::cerr << "Couldn't find the constructor ***";
throwRuntimeException (env, "Can't find Constructor for Exception");
return;
}
jstring strError = env->NewStringUTF(message);
jthrowable ex = (jthrowable)env->NewObject(exceptionClass, constructor, code, strError);
env->Throw(ex);
}
std::string convertJavaString(JNIEnv * env, jstring& jstr)
{
const char * valueStr = env->GetStringUTFChars(jstr, NULL);
std::string data(valueStr);
env->ReleaseStringUTFChars(jstr, valueStr);
return data;
}

View File

@ -1,26 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef JAVAUTILITIES_H_
#define JAVAUTILITIES_H_
#include <string>
#include <jni.h>
void throwException(JNIEnv * env, const int code, const char * message);
std::string convertJavaString(JNIEnv * env, jstring& jstr);
#endif /*JAVAUTILITIES_H_*/

View File

@ -1,39 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LOCKCLASS_H_
#define LOCKCLASS_H_
#include <pthread.h>
class LockClass
{
protected:
pthread_mutex_t* _m;
public:
inline LockClass(pthread_mutex_t* m) : _m(m)
{
::pthread_mutex_lock(_m);
}
inline ~LockClass()
{
::pthread_mutex_unlock(_m);
}
};
#endif /*LOCKCLASS_H_*/

View File

@ -1,24 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _VERSION_NATIVE_AIO
// This definition needs to match org.apache.activemq.artemis.core.asyncio.impl.AsynchronousFileImpl.EXPECTED_NATIVE_VERSION
// Or else the native module won't be loaded because of version mismatches
#define _VERSION_NATIVE_AIO 52
#endif

View File

@ -0,0 +1,23 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
void throwRuntimeException(JNIEnv* env, char* message);
void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void throwIOException(JNIEnv* env, char* message);
void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
void throwClosedChannelException(JNIEnv* env);
void throwOutOfMemoryError(JNIEnv* env);
char* exceptionMessage(char* msg, int error);

View File

@ -0,0 +1,710 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _GNU_SOURCE
// libaio, O_DIRECT and other things won't be available without this define
#define _GNU_SOURCE
#endif
//#define DEBUG
#include <jni.h>
#include <unistd.h>
#include <errno.h>
#include <libaio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#include "org_apache_activemq_artemis_jlibaio_LibaioContext.h"
#include "exception_helper.h"
struct io_control {
io_context_t ioContext;
struct io_event * events;
jobject thisObject;
// This is used to make sure we don't return IOCB while something else is using them
// this is to guarantee the submits could be done concurrently with polling
pthread_mutex_t iocbLock;
pthread_mutex_t pollLock;
// a resuable pool of iocb
struct iocb ** iocb;
int queueSize;
int iocbPut;
int iocbGet;
int used;
};
jclass submitClass = NULL;
jmethodID errorMethod = NULL;
jmethodID doneMethod = NULL;
jmethodID libaioContextDone = NULL;
jclass libaioContextClass = NULL;
jclass runtimeExceptionClass = NULL;
jclass ioExceptionClass = NULL;
// util methods
void throwRuntimeException(JNIEnv* env, char* message) {
(*env)->ThrowNew(env, runtimeExceptionClass, message);
}
void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber);
(*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage);
free(allocatedMessage);
}
void throwIOException(JNIEnv* env, char* message) {
(*env)->ThrowNew(env, ioExceptionClass, message);
}
void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
char* allocatedMessage = exceptionMessage(message, errorNumber);
(*env)->ThrowNew(env, ioExceptionClass, allocatedMessage);
free(allocatedMessage);
}
void throwOutOfMemoryError(JNIEnv* env) {
jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
(*env)->ThrowNew(env, exceptionClass, "");
}
/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */
char* exceptionMessage(char* msg, int error) {
if (error < 0) {
// some functions return negative values
// and it's hard to keep track of when to send -error and when not
// this will just take care when things are forgotten
// what would generate a proper error
error = error * -1;
}
//strerror is returning a constant, so no need to free anything coming from strerror
char* err = strerror(error);
char* result = malloc(strlen(msg) + strlen(err) + 1);
strcpy(result, msg);
strcat(result, err);
return result;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
} else {
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
if (localRuntimeExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
runtimeExceptionClass = (jclass) (*env)->NewGlobalRef(env, localRuntimeExceptionClass);
if (runtimeExceptionClass == NULL) {
// out-of-memory!
throwOutOfMemoryError(env);
return JNI_ERR;
}
jclass localIoExceptionClass = (*env)->FindClass(env, "java/io/IOException");
if (localIoExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
}
ioExceptionClass = (jclass) (*env)->NewGlobalRef(env, localIoExceptionClass);
if (ioExceptionClass == NULL) {
// out-of-memory!
throwOutOfMemoryError(env);
return JNI_ERR;
}
submitClass = (*env)->FindClass(env, "org/apache/activemq/artemis/jlibaio/SubmitInfo");
if (submitClass == NULL) {
return JNI_ERR;
}
submitClass = (jclass)(*env)->NewGlobalRef(env, (jobject)submitClass);
errorMethod = (*env)->GetMethodID(env, submitClass, "onError", "(ILjava/lang/String;)V");
if (errorMethod == NULL) {
return JNI_ERR;
}
errorMethod = (jmethodID)(*env)->NewGlobalRef(env, (jobject)(errorMethod));
doneMethod = (*env)->GetMethodID(env, submitClass, "done", "()V");
if (doneMethod == NULL) {
return JNI_ERR;
}
doneMethod = (jmethodID)(*env)->NewGlobalRef(env, (jobject)(doneMethod));
libaioContextClass = (*env)->FindClass(env, "org/apache/activemq/artemis/jlibaio/LibaioContext");
if (libaioContextClass == NULL) {
return JNI_ERR;
}
libaioContextClass = (jclass)(*env)->NewGlobalRef(env, (jobject)libaioContextClass);
libaioContextDone = (*env)->GetMethodID(env, libaioContextClass, "done", "(Lorg/apache/activemq/artemis/jlibaio/SubmitInfo;)V");
if (libaioContextDone == NULL) {
return JNI_ERR;
}
libaioContextDone = (jmethodID)(*env)->NewGlobalRef(env, (jobject)libaioContextDone);
return JNI_VERSION_1_6;
}
}
void JNI_OnUnload(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
// Something is wrong but nothing we can do about this :(
return;
} else {
// delete global references so the GC can collect them
if (runtimeExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, runtimeExceptionClass);
}
if (ioExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, ioExceptionClass);
}
// Deleting global refs so their classes can be GCed
if (errorMethod != NULL) {
(*env)->DeleteGlobalRef(env, (jobject)errorMethod);
}
if (doneMethod != NULL) {
(*env)->DeleteGlobalRef(env, (jobject)doneMethod);
}
if (submitClass != NULL) {
(*env)->DeleteGlobalRef(env, (jobject)submitClass);
}
if (libaioContextClass != NULL) {
(*env)->DeleteGlobalRef(env, (jobject)libaioContextClass);
}
if (libaioContextDone != NULL) {
(*env)->DeleteGlobalRef(env, (jobject)libaioContextDone);
}
}
}
static inline struct io_control * getIOControl(JNIEnv* env, jobject pointer) {
struct io_control * ioControl = (struct io_control *) (*env)->GetDirectBufferAddress(env, pointer);
if (ioControl == NULL) {
throwRuntimeException(env, "Controller not initialized");
}
return ioControl;
}
/**
* remove an iocb from the pool of IOCBs. Returns null if full
*/
static inline struct iocb * getIOCB(struct io_control * control) {
struct iocb * iocb = 0;
pthread_mutex_lock(&(control->iocbLock));
#ifdef DEBUG
fprintf (stdout, "getIOCB::used=%d, queueSize=%d, get=%d, put=%d\n", control->used, control->queueSize, control->iocbGet, control->iocbPut);
#endif
if (control->used < control->queueSize) {
control->used++;
iocb = control->iocb[control->iocbGet++];
if (control->iocbGet >= control->queueSize) {
control->iocbGet = 0;
}
}
pthread_mutex_unlock(&(control->iocbLock));
return iocb;
}
/**
* Put an iocb back on the pool of IOCBs
*/
static inline void putIOCB(struct io_control * control, struct iocb * iocbBack) {
pthread_mutex_lock(&(control->iocbLock));
#ifdef DEBUG
fprintf (stdout, "putIOCB::used=%d, queueSize=%d, get=%d, put=%d\n", control->used, control->queueSize, control->iocbGet, control->iocbPut);
#endif
control->used--;
control->iocb[control->iocbPut++] = iocbBack;
if (control->iocbPut >= control->queueSize) {
control->iocbPut = 0;
}
pthread_mutex_unlock(&(control->iocbLock));
}
static inline void * getBuffer(JNIEnv* env, jobject pointer) {
return (*env)->GetDirectBufferAddress(env, pointer);
}
JNIEXPORT jboolean JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_lock
(JNIEnv * env, jclass clazz, jint handle) {
return flock(handle, LOCK_EX | LOCK_NB) == 0;
}
/**
* Everything that is allocated here will be freed at deleteContext when the class is unloaded.
*/
JNIEXPORT jobject JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_newContext(JNIEnv* env, jobject thisObject, jint queueSize) {
io_context_t libaioContext;
int i = 0;
#ifdef DEBUG
fprintf (stdout, "Initializing context\n");
#endif
int res = io_queue_init(queueSize, &libaioContext);
if (res) {
// Error, so need to release whatever was done before
free(libaioContext);
throwRuntimeExceptionErrorNo(env, "Cannot initialize queue:", res);
return NULL;
}
struct iocb ** iocb = (struct iocb **)malloc((sizeof(struct iocb *) * (size_t)queueSize));
if (iocb == NULL) {
throwOutOfMemoryError(env);
return NULL;
}
for (i = 0; i < queueSize; i++) {
iocb[i] = (struct iocb *)malloc(sizeof(struct iocb));
if (iocb[i] == NULL) {
// it's unlikely this would happen at this point
// for that reason I'm not cleaning up individual IOCBs here
// we could increase support here with a cleanup of any previously allocated iocb
// But I'm afraid of adding not needed complexity here
throwOutOfMemoryError(env);
return NULL;
}
}
struct io_control * theControl = (struct io_control *) malloc(sizeof(struct io_control));
if (theControl == NULL) {
throwOutOfMemoryError(env);
return NULL;
}
res = pthread_mutex_init(&(theControl->iocbLock), 0);
if (res) {
free(theControl);
free(libaioContext);
throwRuntimeExceptionErrorNo(env, "Can't initialize mutext:", res);
return NULL;
}
res = pthread_mutex_init(&(theControl->pollLock), 0);
if (res) {
free(theControl);
free(libaioContext);
throwRuntimeExceptionErrorNo(env, "Can't initialize mutext:", res);
return NULL;
}
struct io_event * events = (struct io_event *)malloc(sizeof(struct io_event) * (size_t)queueSize);
theControl->ioContext = libaioContext;
theControl->events = events;
theControl->iocb = iocb;
theControl->queueSize = queueSize;
theControl->iocbPut = 0;
theControl->iocbGet = 0;
theControl->used = 0;
theControl->thisObject = (*env)->NewGlobalRef(env, thisObject);
return (*env)->NewDirectByteBuffer(env, theControl, sizeof(struct io_control));
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_deleteContext(JNIEnv* env, jclass clazz, jobject contextPointer) {
int i;
struct io_control * theControl = getIOControl(env, contextPointer);
if (theControl == NULL) {
return;
}
io_queue_release(theControl->ioContext);
// to make sure the poll has finished
pthread_mutex_lock(&(theControl->pollLock));
pthread_mutex_unlock(&(theControl->pollLock));
pthread_mutex_destroy(&(theControl->pollLock));
pthread_mutex_destroy(&(theControl->iocbLock));
// Releasing each individual iocb
for (i = 0; i < theControl->queueSize; i++) {
free(theControl->iocb[i]);
}
(*env)->DeleteGlobalRef(env, theControl->thisObject);
free(theControl->iocb);
free(theControl->events);
free(theControl);
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_close(JNIEnv* env, jclass clazz, jint fd) {
if (close(fd) < 0) {
throwIOExceptionErrorNo(env, "Error closing file:", errno);
}
}
JNIEXPORT int JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_open(JNIEnv* env, jclass clazz,
jstring path, jboolean direct) {
const char* f_path = (*env)->GetStringUTFChars(env, path, 0);
int res;
if (direct) {
res = open(f_path, O_RDWR | O_CREAT | O_DIRECT, 0666);
} else {
res = open(f_path, O_RDWR | O_CREAT, 0666);
}
(*env)->ReleaseStringUTFChars(env, path, f_path);
if (res < 0) {
throwIOExceptionErrorNo(env, "Cannot open file:", errno);
}
return res;
}
static inline void submit(JNIEnv * env, struct io_control * theControl, struct iocb * iocb) {
int result = io_submit(theControl->ioContext, 1, &iocb);
if (result < 0) {
// Putting the Global Ref and IOCB back in case of a failure
(*env)->DeleteGlobalRef(env, (jobject)iocb->data);
putIOCB(theControl, iocb);
throwIOExceptionErrorNo(env, "Error while submitting IO: ", -result);
}
return;
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_submitWrite
(JNIEnv * env, jclass clazz, jint fileHandle, jobject contextPointer, jlong position, jint size, jobject bufferWrite, jobject callback) {
struct io_control * theControl = getIOControl(env, contextPointer);
if (theControl == NULL) {
return;
}
#ifdef DEBUG
fprintf (stdout, "submitWrite position %ld, size %d\n", position, size);
#endif
struct iocb * iocb = getIOCB(theControl);
if (iocb == NULL) {
throwIOException(env, "Not enough space in libaio queue");
return;
}
io_prep_pwrite(iocb, fileHandle, getBuffer(env, bufferWrite), (size_t)size, position);
// The GlobalRef will be deleted when poll is called. this is done so
// the vm wouldn't crash if the Callback passed by the user is GCed between submission
// and callback.
// also as the real intention is to hold the reference until the life cycle is complete
iocb->data = (void *) (*env)->NewGlobalRef(env, callback);
return submit(env, theControl, iocb);
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_submitRead
(JNIEnv * env, jclass clazz, jint fileHandle, jobject contextPointer, jlong position, jint size, jobject bufferRead, jobject callback) {
struct io_control * theControl = getIOControl(env, contextPointer);
if (theControl == NULL) {
return;
}
struct iocb * iocb = getIOCB(theControl);
if (iocb == NULL) {
throwIOException(env, "Not enough space in libaio queue");
return;
}
io_prep_pread(iocb, fileHandle, getBuffer(env, bufferRead), (size_t)size, position);
// The GlobalRef will be deleted when poll is called. this is done so
// the vm wouldn't crash if the Callback passed by the user is GCed between submission
// and callback.
// also as the real intention is to hold the reference until the life cycle is complete
iocb->data = (void *) (*env)->NewGlobalRef(env, callback);
return submit(env, theControl, iocb);
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_blockedPoll
(JNIEnv * env, jobject thisObject, jobject contextPointer) {
#ifdef DEBUG
fprintf (stdout, "Running blockedPoll");
#endif
int i;
struct io_control * theControl = getIOControl(env, contextPointer);
if (theControl == NULL) {
return;
}
int max = theControl->queueSize;
pthread_mutex_lock(&(theControl->pollLock));
for (;;) {
int result = io_getevents(theControl->ioContext, 1, max, theControl->events, 0);
if (result < 0)
{
#ifdef DEBUG
fprintf (stdout, "finished blockedPoll rutine with result=%d\n", result);
#endif
break;
}
#ifdef DEBUG
fprintf (stdout, "blockedPoll returned %d events\n", result);
#endif
for (i = 0; i < result; i++)
{
#ifdef DEBUG
fprintf (stdout, "blockedPoll treading event %d\n", i);
#endif
struct io_event * event = &(theControl->events[i]);
struct iocb * iocbp = event->obj;
int eventResult = (int)event->res;
#ifdef DEBUG
fprintf (stdout, "Poll res: %d totalRes=%d\n", eventResult, result);
#endif
if (eventResult < 0) {
#ifdef DEBUG
fprintf (stdout, "Error: %s\n", strerror(-eventResult));
#endif
jstring jstrError = (*env)->NewStringUTF(env, strerror(-eventResult));
if (iocbp->data != NULL) {
(*env)->CallVoidMethod(env, (jobject)(iocbp->data), errorMethod, (jint)(-eventResult), jstrError);
}
}
jobject obj = (jobject)iocbp->data;
putIOCB(theControl, iocbp);
if (obj != NULL) {
(*env)->CallVoidMethod(env, theControl->thisObject, libaioContextDone,obj);
// We delete the globalRef after the completion of the callback
(*env)->DeleteGlobalRef(env, obj);
}
}
}
pthread_mutex_unlock(&(theControl->pollLock));
}
JNIEXPORT jint JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_poll
(JNIEnv * env, jobject obj, jobject contextPointer, jobjectArray callbacks, jint min, jint max) {
int i = 0;
struct io_control * theControl = getIOControl(env, contextPointer);
if (theControl == NULL) {
return 0;
}
int result = io_getevents(theControl->ioContext, min, max, theControl->events, 0);
int retVal = result;
for (i = 0; i < result; i++) {
struct io_event * event = &(theControl->events[i]);
struct iocb * iocbp = event->obj;
int eventResult = (int)event->res;
#ifdef DEBUG
fprintf (stdout, "Poll res: %d totalRes=%d\n", eventResult, result);
#endif
if (eventResult < 0) {
#ifdef DEBUG
fprintf (stdout, "Error: %s\n", strerror(-eventResult));
#endif
jstring jstrError = (*env)->NewStringUTF(env, strerror(-eventResult));
(*env)->CallVoidMethod(env, (jobject)(iocbp->data), errorMethod, (jint)(-eventResult), jstrError);
}
(*env)->SetObjectArrayElement(env, callbacks, i, (jobject)iocbp->data);
if (iocbp->data != NULL) {
// We delete the globalRef after the completion of the callback
(*env)->DeleteGlobalRef(env, (jobject)iocbp->data);
}
putIOCB(theControl, iocbp);
}
return retVal;
}
JNIEXPORT jobject JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_newAlignedBuffer
(JNIEnv * env, jclass clazz, jint size, jint alignment) {
if (size % alignment != 0) {
throwRuntimeException(env, "Buffer size needs to be aligned to passed argument");
return NULL;
}
// This will allocate a buffer, aligned by alignment.
// Buffers created here need to be manually destroyed by destroyBuffer, or this would leak on the process heap away of Java's GC managed memory
// NOTE: this buffer will contain non initialized data, you must fill it up properly
void * buffer;
int result = posix_memalign(&buffer, (size_t)alignment, (size_t)size);
if (result) {
throwRuntimeExceptionErrorNo(env, "Can't allocate posix buffer:", result);
return NULL;
}
memset(buffer, 0, (size_t)size);
return (*env)->NewDirectByteBuffer(env, buffer, size);
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_freeBuffer
(JNIEnv * env, jclass clazz, jobject jbuffer) {
if (jbuffer == NULL)
{
throwRuntimeException(env, "Null pointer");
return;
}
void * buffer = (*env)->GetDirectBufferAddress(env, jbuffer);
free(buffer);
}
/** It does nothing... just return true to make sure it has all the binary dependencies */
JNIEXPORT jint JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_getNativeVersion
(JNIEnv * env, jclass clazz)
{
return org_apache_activemq_artemis_jlibaio_LibaioContext_EXPECTED_NATIVE_VERSION;
}
JNIEXPORT jlong JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_getSize
(JNIEnv * env, jclass clazz, jint fd)
{
struct stat statBuffer;
if (fstat(fd, &statBuffer) < 0)
{
throwIOExceptionErrorNo(env, "Cannot determine file size:", errno);
return -1l;
}
return statBuffer.st_size;
}
JNIEXPORT jint JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_getBlockSizeFD
(JNIEnv * env, jclass clazz, jint fd)
{
struct stat statBuffer;
if (fstat(fd, &statBuffer) < 0)
{
throwIOExceptionErrorNo(env, "Cannot determine file size:", errno);
return -1l;
}
return statBuffer.st_blksize;
}
JNIEXPORT jint JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_getBlockSize
(JNIEnv * env, jclass clazz, jstring path)
{
const char* f_path = (*env)->GetStringUTFChars(env, path, 0);
struct stat statBuffer;
if (stat(f_path, &statBuffer) < 0)
{
throwIOExceptionErrorNo(env, "Cannot determine file size:", errno);
return -1l;
}
(*env)->ReleaseStringUTFChars(env, path, f_path);
return statBuffer.st_blksize;
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_fallocate
(JNIEnv * env, jclass clazz, jint fd, jlong size)
{
if (fallocate(fd, 0, 0, (off_t) size) < 0)
{
throwIOExceptionErrorNo(env, "Could not preallocate file", errno);
}
fsync(fd);
lseek (fd, 0, SEEK_SET);
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_fill
(JNIEnv * env, jclass clazz, jint fd, jlong size)
{
void * preAllocBuffer = 0;
if (posix_memalign(&preAllocBuffer, 512, size) != 0)
{
throwOutOfMemoryError(env);
return;
}
memset(preAllocBuffer, 0, size);
lseek (fd, 0, SEEK_SET);
write(fd, preAllocBuffer, size);
lseek (fd, 0, SEEK_SET);
free (preAllocBuffer);
}
JNIEXPORT void JNICALL Java_org_apache_activemq_artemis_jlibaio_LibaioContext_memsetBuffer
(JNIEnv *env, jclass clazz, jobject jbuffer, jint size)
{
#ifdef DEBUG
fprintf (stdout, "Mem setting buffer with %d bytes\n", size);
#endif
void * buffer = (*env)->GetDirectBufferAddress(env, jbuffer);
if (buffer == 0)
{
throwRuntimeException(env, "Invalid Buffer used, libaio requires NativeBuffer instead of Java ByteBuffer");
return;
}
memset(buffer, 0, (size_t)size);
}

View File

@ -1,74 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.libaio;
import java.nio.ByteBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException;
public class Native
{
// Functions used for locking files .....
public static native int openFile(String fileName);
public static native void closeFile(int handle);
public static native boolean flock(int handle);
// Functions used for locking files ^^^^^^^^
public static native void resetBuffer(ByteBuffer directByteBuffer, int size);
public static native void destroyBuffer(ByteBuffer buffer);
public static native ByteBuffer newNativeBuffer(long size);
public static native void newInit(Class someClass);
public static native ByteBuffer init(Class controllerClass, String fileName, int maxIO, Object logger) throws ActiveMQException;
public static native long size0(ByteBuffer handle);
public static native void write(Object thisObject, ByteBuffer handle,
long sequence,
long position,
long size,
ByteBuffer buffer,
Object aioPackageCallback) throws ActiveMQException;
/** a direct write to the file without the use of libaio's submit. */
public static native void writeInternal(ByteBuffer handle, long positionToWrite, long size, ByteBuffer bytes) throws ActiveMQException;
/**
*This is using org.apache.activemq.artemis.core.asyncio.AIOCallback
*/
public static native void read(Object thisObject, ByteBuffer handle, long position, long size, ByteBuffer buffer, Object aioPackageCallback) throws ActiveMQException;
public static native void fill(ByteBuffer handle, long position, int blocks, long size, byte fillChar) throws ActiveMQException;
public static native void closeInternal(ByteBuffer handler);
public static native void stopPoller(ByteBuffer handler);
/** A native method that does nothing, and just validate if the ELF dependencies are loaded and on the correct platform as this binary format */
public static native int getNativeVersion();
/** Poll asynchronous events from internal queues */
public static native void internalPollEvents(ByteBuffer handler);
// Inner classes ---------------------------------------------------------------------
}

View File

@ -0,0 +1,446 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.jlibaio;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* This class is used as an aggregator for the {@link LibaioFile}.
* <br>
* It holds native data, and it will share a libaio queue that can be used by multiple files.
* <br>
* You need to use the poll methods to read the result of write and read submissions.
* <br>
* You also need to use the special buffer created by {@link LibaioFile} as you need special alignments
* when dealing with O_DIRECT files.
* <br>
* A Single controller can server multiple files. There's no need to create one controller per file.
* <br>
* <a href="https://ext4.wiki.kernel.org/index.php/Clarifying_Direct_IO's_Semantics">Interesting reading for this.</a>
*/
public class LibaioContext <Callback extends SubmitInfo> implements Closeable
{
private static final AtomicLong totalMaxIO = new AtomicLong(0);
/**
* This definition needs to match Version.h on the native sources.
* <br>
* Or else the native module won't be loaded because of version mismatches
*/
private static final int EXPECTED_NATIVE_VERSION = 1;
private static boolean loaded = false;
public static boolean isLoaded()
{
return loaded;
}
private static boolean loadLibrary(final String name)
{
try
{
System.loadLibrary(name);
if (getNativeVersion() != EXPECTED_NATIVE_VERSION)
{
NativeLogger.LOGGER.incompatibleNativeLibrary();
return false;
}
else
{
return true;
}
}
catch (Throwable e)
{
NativeLogger.LOGGER.debug(name + " -> error loading the native library", e);
return false;
}
}
static
{
String[] libraries = new String[]{"artemis-native-64", "artemis-native-32"};
for (String library : libraries)
{
if (loadLibrary(library))
{
loaded = true;
break;
}
else
{
NativeLogger.LOGGER.debug("Library " + library + " not found!");
}
}
if (!loaded)
{
NativeLogger.LOGGER.debug("Couldn't locate LibAIO Wrapper");
}
}
/**
* This is used to validate leaks on tests.
* @return the number of allocated aio, to be used on test checks.
*/
public static long getTotalMaxIO()
{
return totalMaxIO.get();
}
/**
* It will reset all the positions on the buffer to 0, using memset.
* @param buffer a native buffer.
s */
public void memsetBuffer(ByteBuffer buffer)
{
memsetBuffer(buffer, buffer.limit());
}
/**
* This is used on tests validating for leaks.
*/
public static void resetMaxAIO()
{
totalMaxIO.set(0);
}
/**
* the native ioContext including the structure created.
*/
private final ByteBuffer ioContext;
private final AtomicBoolean closed = new AtomicBoolean(false);
final Semaphore ioSpace;
final int queueSize;
/**
* The queue size here will use resources defined on the kernel parameter
* <a href="https://www.kernel.org/doc/Documentation/sysctl/fs.txt">fs.aio-max-nr</a> .
*
* @param queueSize the size to be initialize on libaio
* io_queue_init which can't be higher than /proc/sys/fs/aio-max-nr.
* @param useSemaphore should block on a semaphore avoiding using more submits than what's available.
*/
public LibaioContext(int queueSize, boolean useSemaphore)
{
try
{
this.ioContext = newContext(queueSize);
}
catch (Exception e)
{
throw e;
}
this.queueSize = queueSize;
totalMaxIO.addAndGet(queueSize);
if (useSemaphore)
{
this.ioSpace = new Semaphore(queueSize);
}
else
{
this.ioSpace = null;
}
}
/**
* Documented at {@link LibaioFile#write(long, int, java.nio.ByteBuffer, SubmitInfo)}
* @param fd the file descriptor
* @param position the write position
* @param size number of bytes to use
* @param bufferWrite the native buffer
* @param callback a callback
* @throws IOException in case of error
*/
public void submitWrite(int fd,long position, int size,
ByteBuffer bufferWrite, Callback callback) throws IOException
{
try
{
if (ioSpace != null)
{
ioSpace.acquire();
}
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
throw new IOException(e.getMessage(), e);
}
submitWrite(fd, this.ioContext, position, size, bufferWrite, callback);
}
public void submitRead(int fd, long position, int size, ByteBuffer bufferWrite,
Callback callback) throws IOException
{
try
{
if (ioSpace != null)
{
ioSpace.acquire();
}
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
throw new IOException(e.getMessage(), e);
}
submitRead(fd, this.ioContext, position, size, bufferWrite, callback);
}
/**
* This is used to close the libaio queues and cleanup the native data used.
* <br>
* It is unsafe to close the controller while you have pending writes or files open as
* this could cause core dumps or VM crashes.
*/
@Override
public void close()
{
if (!closed.getAndSet(true))
{
totalMaxIO.addAndGet(-queueSize);
if (ioContext != null)
{
deleteContext(ioContext);
}
}
}
@Override
protected void finalize() throws Throwable
{
super.finalize();
close();
}
/**
* It will open a file. If you set the direct flag = false then you won't need to use the special buffer.
* Notice: This will create an empty file if the file doesn't already exist.
*
* @param file the file to be open.
* @param direct will set ODIRECT.
* @return It will return a LibaioFile instance.
* @throws IOException in case of error.
*/
public LibaioFile<Callback> openFile(File file, boolean direct) throws IOException
{
return openFile(file.getPath(), direct);
}
/**
* It will open a file. If you set the direct flag = false then you won't need to use the special buffer.
* Notice: This will create an empty file if the file doesn't already exist.
*
* @param file the file to be open.
* @param direct should use O_DIRECT when opening the file.
* @return a new open file.
* @throws IOException in case of error.
*/
public LibaioFile<Callback> openFile(String file, boolean direct) throws IOException
{
checkNotNull(file, "path");
checkNotNull(ioContext, "IOContext");
// note: the native layer will throw an IOException in case of errors
int res = LibaioContext.open(file, direct);
return new LibaioFile<>(res, this);
}
/**
* It will open a file disassociated with any sort of factory.
* This is useful when you won't use reading / writing through libaio like locking files.
* @param file a file name
* @param direct will use O_DIRECT
* @return a new file
* @throws IOException in case of error.
*/
public static LibaioFile openControlFile(String file, boolean direct) throws IOException
{
checkNotNull(file, "path");
// note: the native layer will throw an IOException in case of errors
int res = LibaioContext.open(file, direct);
return new LibaioFile<>(res, null);
}
/**
* It will poll the libaio queue for results. It should block until min is reached
* Results are placed on the callback.
* <br>
* This shouldn't be called concurrently. You should provide your own synchronization if you need more than one
* Thread polling for any reason.
* <br>
* Notice that the native layer will invoke {@link SubmitInfo#onError(int, String)} in case of failures,
* but it won't call done method for you.
*
* @param callbacks area to receive the callbacks passed on submission.The size of this callback has to
* be greater than the parameter max.
* @param min the minimum number of elements to receive. It will block until this is achieved.
* @param max The maximum number of elements to receive.
* @return Number of callbacks returned.
* @see LibaioFile#write(long, int, java.nio.ByteBuffer, SubmitInfo)
* @see LibaioFile#read(long, int, java.nio.ByteBuffer, SubmitInfo)
*/
public int poll(Callback[] callbacks, int min, int max)
{
int released = poll(ioContext, callbacks, min, max);
if (ioSpace != null)
{
if (released > 0)
{
ioSpace.release(released);
}
}
return released;
}
/**
* It will start polling and will keep doing until the context is closed.
* This will call callbacks on {@link SubmitInfo#onError(int, String)} and
* {@link SubmitInfo#done()}.
* In case of error, both {@link SubmitInfo#onError(int, String)} and
* {@link SubmitInfo#done()} are called.
*/
public void poll()
{
blockedPoll(ioContext);
}
/** Called from the native layer */
private void done(SubmitInfo info)
{
if (ioSpace != null)
{
ioSpace.release();
}
info.done();
}
/**
* This is the queue for libaio, initialized with queueSize.
*/
private native ByteBuffer newContext(int queueSize);
/**
* Internal method to be used when closing the controller.
*/
private native void deleteContext(ByteBuffer buffer);
/**
* it will return a file descriptor.
*
* @param path the file name.
* @param direct translates as O_DIRECT On open
* @return a fd from open C call.
*/
public static native int open(String path, boolean direct);
static native void close(int fd);
/**
*/
/**
* Buffers for O_DIRECT need to use posix_memalign.
* <br>
* Documented at {@link LibaioFile#newBuffer(int)}.
*
* @param size needs to be % alignment
* @param alignment the alignment used at the dispositive
* @return a new native buffer used with posix_memalign
*/
public static native ByteBuffer newAlignedBuffer(int size, int alignment);
/**
* This will call posix free to release the inner buffer allocated at {@link #newAlignedBuffer(int, int)}.
* @param buffer a native buffer allocated with {@link #newAlignedBuffer(int, int)}.
*/
public static native void freeBuffer(ByteBuffer buffer);
/**
* Documented at {@link LibaioFile#write(long, int, java.nio.ByteBuffer, SubmitInfo)}.
*/
native void submitWrite(int fd,
ByteBuffer libaioContext,
long position, int size, ByteBuffer bufferWrite,
Callback callback) throws IOException;
/**
* Documented at {@link LibaioFile#read(long, int, java.nio.ByteBuffer, SubmitInfo)}.
*/
native void submitRead(int fd,
ByteBuffer libaioContext,
long position, int size, ByteBuffer bufferWrite,
Callback callback) throws IOException;
/**
* Note: this shouldn't be done concurrently.
* This method will block until the min condition is satisfied on the poll.
* <p/>
* The callbacks will include the original callback sent at submit (read or write).
*/
native int poll(ByteBuffer libaioContext, Callback[] callbacks, int min, int max);
/**
* This method will block as long as the context is open.
*/
native void blockedPoll(ByteBuffer libaioContext);
static native int getNativeVersion();
public static native boolean lock(int fd);
public static native void memsetBuffer(ByteBuffer buffer, int size);
static native long getSize(int fd);
static native int getBlockSizeFD(int fd);
public static int getBlockSize(File path)
{
return getBlockSize(path.getAbsolutePath());
}
public static native int getBlockSize(String path);
static native void fallocate(int fd, long size);
static native void fill(int fd, long size);
static native void writeInternal(int fd, long position, long size, ByteBuffer bufferWrite) throws IOException;
}

View File

@ -0,0 +1,152 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.jlibaio;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* This is an extension to use libaio.
*/
public final class LibaioFile<Callback extends SubmitInfo>
{
protected boolean open;
/**
* This represents a structure allocated on the native
* this is a io_context_t
*/
final LibaioContext<Callback> ctx;
private int fd;
LibaioFile(int fd, LibaioContext ctx)
{
this.ctx = ctx;
this.fd = fd;
}
public int getBlockSize()
{
return 512;
// FIXME
//return LibaioContext.getBlockSizeFD(fd);
}
public boolean lock()
{
return LibaioContext.lock(fd);
}
/**
* {@inheritDoc}
*/
public void close() throws IOException
{
open = false;
LibaioContext.close(fd);
}
/**
* @return The size of the file.
*/
public long getSize()
{
return LibaioContext.getSize(fd);
}
/**
* It will submit a write to the queue. The callback sent here will be received on the
* {@link LibaioContext#poll(SubmitInfo[], int, int)}
* In case of the libaio queue is full (e.g. returning E_AGAIN) this method will return false.
* <br>
* Notice: this won't hold a global reference on buffer, callback should hold a reference towards bufferWrite.
* And don't free the buffer until the callback was called as this could crash the VM.
*
* @param position The position on the file to write. Notice this has to be a multiple of 512.
* @param size The size of the buffer to use while writing.
* @param buffer if you are using O_DIRECT the buffer here needs to be allocated by {@link #newBuffer(int)}.
* @param callback A callback to be returned on the poll method.
* @throws java.io.IOException in case of error
*/
public void write(long position, int size, ByteBuffer buffer, Callback callback) throws IOException
{
ctx.submitWrite(fd, position, size, buffer, callback);
}
/**
* It will submit a read to the queue. The callback sent here will be received on the
* {@link LibaioContext#poll(SubmitInfo[], int, int)}.
* In case of the libaio queue is full (e.g. returning E_AGAIN) this method will return false.
* <br>
* Notice: this won't hold a global reference on buffer, callback should hold a reference towards bufferWrite.
* And don't free the buffer until the callback was called as this could crash the VM.
* *
*
* @param position The position on the file to read. Notice this has to be a multiple of 512.
* @param size The size of the buffer to use while reading.
* @param buffer if you are using O_DIRECT the buffer here needs to be allocated by {@link #newBuffer(int)}.
* @param callback A callback to be returned on the poll method.
* @throws java.io.IOException in case of error
* @see LibaioContext#poll(SubmitInfo[], int, int)
*/
public void read(long position, int size, ByteBuffer buffer, Callback callback) throws IOException
{
ctx.submitRead(fd, position, size, buffer, callback);
}
/**
* It will allocate a buffer to be used on libaio operations.
* Buffers here are allocated with posix_memalign.
* <br>
* You need to explicitly free the buffer created from here using the
* {@link LibaioContext#freeBuffer(java.nio.ByteBuffer)}.
*
* @param size the size of the buffer.
* @return the buffer allocated.
*/
public ByteBuffer newBuffer(int size)
{
return LibaioContext.newAlignedBuffer(size, 512);
}
/**
* It will preallocate the file with a given size.
* @param size number of bytes to be filled on the file
*/
public void fill(long size)
{
try
{
LibaioContext.fill(fd, size);
}
catch (OutOfMemoryError e)
{
NativeLogger.LOGGER.debug("Didn't have enough memory to allocate " + size + " bytes in memory, using simple fallocate");
LibaioContext.fallocate(fd, size);
}
}
/**
* It will use fallocate to initialize a file.
* @param size number of bytes to be filled on the file
*/
public void fallocate(long size)
{
LibaioContext.fallocate(fd, size);
}
}

View File

@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.jlibaio;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
/**
* Logger Code 14
*
* each message id must be 6 digits long starting with 14, the 3rd digit donates the level so
*
* INF0 1
* WARN 2
* DEBUG 3
* ERROR 4
* TRACE 5
* FATAL 6
*
* so an INFO message would be 1000 to 6000
*/
@MessageLogger(projectCode = "jlibaio")
public interface NativeLogger extends BasicLogger
{
/**
* The journal logger.
*/
NativeLogger LOGGER = Logger.getMessageLogger(NativeLogger.class, NativeLogger.class.getPackage().getName());
@LogMessage(level = Logger.Level.WARN)
@Message(id = 1001, value = "You have a native library with a different version than expected", format = Message.Format.MESSAGE_FORMAT)
void incompatibleNativeLibrary();
}

View File

@ -14,14 +14,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.asyncio;
import java.nio.ByteBuffer; package org.apache.activemq.artemis.jlibaio;
/** public interface SubmitInfo
* Used to receive a notification on completed buffers used by the AIO layer.
*/
public interface BufferCallback
{ {
void bufferDone(ByteBuffer buffer); void onError(int errno, String message);
void done();
} }

View File

@ -14,14 +14,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.activemq.artemis.core.journal;
import org.apache.activemq.artemis.core.asyncio.AIOCallback;
/** /**
* This class is just a direct extension of AIOCallback. * This packages handles Linux libaio at a low level.
* Just to avoid the direct dependency of org.apache.activemq.artemis.core.asynciio.AIOCallback from the journal. * <br>
* Buffers needs to be specially allocated by {@link org.apache.activemq.artemis.jlibaio.LibaioContext#newAlignedBuffer(int, int)}
* as they need to be aligned to 512 or 4096 when using Direct files.
*/ */
public interface IOAsyncTask extends AIOCallback package org.apache.activemq.artemis.jlibaio;
{
}

View File

@ -0,0 +1,93 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.jlibaio.util;
import org.apache.activemq.artemis.jlibaio.SubmitInfo;
/**
* this is an utility class where you can reuse Callbackk objects for your LibaioContext usage.
*/
public class CallbackCache<Callback extends SubmitInfo>
{
private final SubmitInfo[] pool;
private int put = 0;
private int get = 0;
private int available = 0;
private final int size;
private final Object lock = new Object();
public CallbackCache(int size)
{
this.pool = new SubmitInfo[size];
this.size = size;
}
public Callback get()
{
synchronized (lock)
{
if (available <= 0)
{
return null;
}
else
{
Callback retValue = (Callback)pool[get];
pool[get] = null;
if (retValue == null)
{
throw new NullPointerException("You should initialize the pool before using it");
}
if (retValue != null)
{
available--;
get++;
if (get >= size)
{
get = 0;
}
}
return retValue;
}
}
}
public CallbackCache put(Callback callback)
{
if (callback == null)
{
return null;
}
synchronized (lock)
{
if (available < size)
{
available++;
pool[put++] = callback;
if (put >= size)
{
put = 0;
}
}
}
return this;
}
}

View File

@ -0,0 +1,112 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.jlibaio.test;
import java.util.HashSet;
import org.apache.activemq.artemis.jlibaio.util.CallbackCache;
import org.apache.activemq.artemis.jlibaio.SubmitInfo;
import org.junit.Assert;
import org.junit.Test;
public class CallbackCachelTest
{
@Test
public void testPartiallyInitialized()
{
CallbackCache<MyPool> pool = new CallbackCache(100);
for (int i = 0; i < 50; i++)
{
pool.put(new MyPool(i));
}
MyPool value = pool.get();
Assert.assertNotNull(value);
pool.put(value);
// add and remove immediately
for (int i = 0; i < 777; i++)
{
pool.put(pool.get());
}
HashSet<MyPool> hashValues = new HashSet<>();
MyPool getValue;
while ((getValue = pool.get()) != null)
{
hashValues.add(getValue);
}
Assert.assertEquals(50, hashValues.size());
}
static class MyPool implements SubmitInfo
{
public final int i;
MyPool(int i)
{
this.i = i;
}
public int getI()
{
return i;
}
@Override
public void onError(int errno, String message)
{
}
@Override
public void done()
{
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyPool myPool = (MyPool) o;
if (i != myPool.i) return false;
return true;
}
@Override
public int hashCode()
{
return i;
}
}
}

View File

@ -0,0 +1,859 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.jlibaio.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.jlibaio.LibaioContext;
import org.apache.activemq.artemis.jlibaio.LibaioFile;
import org.apache.activemq.artemis.jlibaio.SubmitInfo;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
/**
* This test is using a different package from {@link LibaioFile}
* as I need to validate public methods on the API
*/
public class LibaioTest
{
@BeforeClass
public static void testAIO()
{
Assume.assumeTrue(LibaioContext.isLoaded());
}
/** This is just an arbitrary number for a number of elements you need to pass to the libaio init method
* Some of the tests are using half of this number, so if anyone decide to change this please use an even number.
*/
private static final int LIBAIO_QUEUE_SIZE = 50;
@Rule
public TemporaryFolder temporaryFolder;
public LibaioContext<TestInfo> control;
@Before
public void setUpFactory()
{
control = new LibaioContext<>(LIBAIO_QUEUE_SIZE, true);
}
@After
public void deleteFactory()
{
control.close();
validateLibaio();
}
public void validateLibaio()
{
Assert.assertEquals(0, LibaioContext.getTotalMaxIO());
}
public LibaioTest()
{
/*
* I didn't use /tmp for three reasons
* - Most systems now will use tmpfs which is not compatible with O_DIRECT
* - This would fill up /tmp in case of failures.
* - target is cleaned up every time you do a mvn clean, so it's safer
*/
File parent = new File("./target");
parent.mkdirs();
temporaryFolder = new TemporaryFolder(parent);
}
@Test
public void testOpen() throws Exception
{
LibaioFile fileDescriptor = control.openFile(temporaryFolder.newFile("test.bin"), true);
fileDescriptor.close();
}
@Test
public void testInitAndFallocate() throws Exception
{
LibaioFile fileDescriptor = control.openFile(temporaryFolder.newFile("test.bin"), true);
fileDescriptor.fallocate(1024 * 1024);
ByteBuffer buffer = fileDescriptor.newBuffer(1024 * 1024);
fileDescriptor.read(0, 1024 * 1024, buffer, new TestInfo());
TestInfo[] callbacks = new TestInfo[1];
control.poll(callbacks, 1, 1);
fileDescriptor.close();
buffer.position(0);
LibaioFile fileDescriptor2 = control.openFile(temporaryFolder.newFile("test2.bin"), true);
fileDescriptor2.fill(1024 * 1024);
fileDescriptor2.read(0, 1024 * 1024, buffer, new TestInfo());
control.poll(callbacks, 1, 1);
for (int i = 0; i < 1024 * 1024; i++)
{
Assert.assertEquals(0, buffer.get());
}
LibaioContext.freeBuffer(buffer);
}
@Test
public void testSubmitWriteOnTwoFiles() throws Exception
{
File file1 = temporaryFolder.newFile("test.bin");
File file2 = temporaryFolder.newFile("test2.bin");
fillupFile(file1, LIBAIO_QUEUE_SIZE / 2);
fillupFile(file2, LIBAIO_QUEUE_SIZE / 2);
LibaioFile[] fileDescriptor = new LibaioFile[]{control.openFile(file1, true),
control.openFile(file2, true)};
Assert.assertEquals((LIBAIO_QUEUE_SIZE / 2) * 512, fileDescriptor[0].getSize());
Assert.assertEquals((LIBAIO_QUEUE_SIZE / 2) * 512, fileDescriptor[1].getSize());
Assert.assertEquals(fileDescriptor[0].getBlockSize(), fileDescriptor[1].getBlockSize());
Assert.assertEquals(LibaioContext.getBlockSize(temporaryFolder.getRoot()), LibaioContext.getBlockSize(file1));
Assert.assertEquals(LibaioContext.getBlockSize(file1), LibaioContext.getBlockSize(file2));
System.out.println("blockSize = " + fileDescriptor[0].getBlockSize());
System.out.println("blockSize /tmp= " + LibaioContext.getBlockSize("/tmp"));
ByteBuffer buffer = LibaioContext.newAlignedBuffer(512, 512);
try
{
for (int i = 0; i < 512; i++)
{
buffer.put((byte) 'a');
}
TestInfo callback = new TestInfo();
TestInfo[] callbacks = new TestInfo[LIBAIO_QUEUE_SIZE];
for (int i = 0; i < LIBAIO_QUEUE_SIZE / 2; i++)
{
for (LibaioFile file : fileDescriptor)
{
file.write(i * 512, 512, buffer, callback);
}
}
Assert.assertEquals(LIBAIO_QUEUE_SIZE, control.poll(callbacks, LIBAIO_QUEUE_SIZE, LIBAIO_QUEUE_SIZE));
for (Object returnedCallback : callbacks)
{
Assert.assertSame(returnedCallback, callback);
}
for (LibaioFile file : fileDescriptor)
{
ByteBuffer bigbuffer = LibaioContext.newAlignedBuffer(512 * 25, 512);
file.read(0, 512 * 25, bigbuffer, callback);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
for (Object returnedCallback : callbacks)
{
Assert.assertSame(returnedCallback, callback);
}
for (int i = 0; i < 512 * 25; i++)
{
Assert.assertEquals((byte) 'a', bigbuffer.get());
}
LibaioContext.freeBuffer(bigbuffer);
file.close();
}
}
finally
{
LibaioContext.freeBuffer(buffer);
}
}
@Test
public void testSubmitWriteAndRead() throws Exception
{
TestInfo callback = new TestInfo();
TestInfo[] callbacks = new TestInfo[LIBAIO_QUEUE_SIZE];
LibaioFile fileDescriptor = control.openFile(temporaryFolder.newFile("test.bin"), true);
// ByteBuffer buffer = ByteBuffer.allocateDirect(512);
ByteBuffer buffer = LibaioContext.newAlignedBuffer(512, 512);
try
{
for (int i = 0; i < 512; i++)
{
buffer.put((byte) 'a');
}
buffer.rewind();
fileDescriptor.write(0, 512, buffer, callback);
int retValue = control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE);
Assert.assertEquals(1, retValue);
Assert.assertSame(callback, callbacks[0]);
LibaioContext.freeBuffer(buffer);
buffer = LibaioContext.newAlignedBuffer(512, 512);
for (int i = 0; i < 512; i++)
{
buffer.put((byte) 'B');
}
fileDescriptor.write(0, 512, buffer, null);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
buffer.rewind();
fileDescriptor.read(0, 512, buffer, null);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
for (int i = 0; i < 512; i++)
{
Assert.assertEquals('B', buffer.get());
}
}
finally
{
LibaioContext.freeBuffer(buffer);
fileDescriptor.close();
}
}
@Test
/**
* This file is making use of libaio without O_DIRECT
* We won't need special buffers on this case.
*/
public void testSubmitWriteAndReadRegularBuffers() throws Exception
{
TestInfo callback = new TestInfo();
TestInfo[] callbacks = new TestInfo[LIBAIO_QUEUE_SIZE];
File file = temporaryFolder.newFile("test.bin");
fillupFile(file, LIBAIO_QUEUE_SIZE);
LibaioFile fileDescriptor = control.openFile(file, false);
final int BUFFER_SIZE = 50;
ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
try
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
buffer.put((byte) 'a');
}
buffer.rewind();
fileDescriptor.write(0, BUFFER_SIZE, buffer, callback);
int retValue = control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE);
System.out.println("Return from poll::" + retValue);
Assert.assertEquals(1, retValue);
Assert.assertSame(callback, callbacks[0]);
buffer.rewind();
for (int i = 0; i < BUFFER_SIZE; i++)
{
buffer.put((byte) 'B');
}
fileDescriptor.write(0, BUFFER_SIZE, buffer, null);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
buffer.rewind();
fileDescriptor.read(0, 50, buffer, null);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
for (int i = 0; i < BUFFER_SIZE; i++)
{
Assert.assertEquals('B', buffer.get());
}
}
finally
{
fileDescriptor.close();
}
}
@Test
public void testSubmitRead() throws Exception
{
TestInfo callback = new TestInfo();
TestInfo[] callbacks = new TestInfo[LIBAIO_QUEUE_SIZE];
File file = temporaryFolder.newFile("test.bin");
fillupFile(file, LIBAIO_QUEUE_SIZE);
LibaioFile fileDescriptor = control.openFile(file, true);
ByteBuffer buffer = LibaioContext.newAlignedBuffer(512, 512);
final int BUFFER_SIZE = 512;
try
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
buffer.put((byte) '@');
}
fileDescriptor.write(0, BUFFER_SIZE, buffer, callback);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
Assert.assertSame(callback, callbacks[0]);
buffer.rewind();
fileDescriptor.read(0, BUFFER_SIZE, buffer, callback);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
Assert.assertSame(callback, callbacks[0]);
for (int i = 0; i < BUFFER_SIZE; i++)
{
Assert.assertEquals('@', buffer.get());
}
}
finally
{
LibaioContext.freeBuffer(buffer);
fileDescriptor.close();
}
}
@Test
public void testInvalidWrite() throws Exception
{
TestInfo callback = new TestInfo();
TestInfo[] callbacks = new TestInfo[LIBAIO_QUEUE_SIZE];
File file = temporaryFolder.newFile("test.bin");
fillupFile(file, LIBAIO_QUEUE_SIZE);
LibaioFile fileDescriptor = control.openFile(file, true);
try
{
ByteBuffer buffer = ByteBuffer.allocateDirect(300);
for (int i = 0; i < 300; i++)
{
buffer.put((byte) 'z');
}
fileDescriptor.write(0, 300, buffer, callback);
Assert.assertEquals(1, control.poll(callbacks, 1, LIBAIO_QUEUE_SIZE));
Assert.assertTrue(callbacks[0].isError());
// Error condition
Assert.assertSame(callbacks[0], callback);
System.out.println("Error:" + callbacks[0]);
buffer = fileDescriptor.newBuffer(512);
for (int i = 0; i < 512; i++)
{
buffer.put((byte) 'z');
}
callback = new TestInfo();
fileDescriptor.write(0, 512, buffer, callback);
Assert.assertEquals(1, control.poll(callbacks, 1, 1));
Assert.assertSame(callback, callbacks[0]);
fileDescriptor.write(5, 512, buffer, callback);
Assert.assertEquals(1, control.poll(callbacks, 1, 1));
Assert.assertTrue(callbacks[0].isError());
callbacks = null;
callback = null;
TestInfo.checkLeaks();
}
finally
{
fileDescriptor.close();
}
}
@Test
public void testLeaks() throws Exception
{
File file = temporaryFolder.newFile("test.bin");
fillupFile(file, LIBAIO_QUEUE_SIZE * 2);
TestInfo[] callbacks = new TestInfo[LIBAIO_QUEUE_SIZE];
LibaioFile<TestInfo> fileDescriptor = control.openFile(file, true);
ByteBuffer bufferWrite = LibaioContext.newAlignedBuffer(512, 512);
try
{
for (int i = 0; i < 512; i++)
{
bufferWrite.put((byte) 'B');
}
for (int j = 0; j < LIBAIO_QUEUE_SIZE * 2; j++)
{
for (int i = 0; i < LIBAIO_QUEUE_SIZE; i++)
{
TestInfo countClass = new TestInfo();
fileDescriptor.write(i * 512, 512, bufferWrite, countClass);
}
Assert.assertEquals(LIBAIO_QUEUE_SIZE, control.poll(callbacks, LIBAIO_QUEUE_SIZE, LIBAIO_QUEUE_SIZE));
for (int i = 0; i < LIBAIO_QUEUE_SIZE; i++)
{
Assert.assertNotNull(callbacks[i]);
callbacks[i] = null;
}
}
TestInfo.checkLeaks();
}
finally
{
LibaioContext.freeBuffer(bufferWrite);
}
}
@Test
public void testLock() throws Exception
{
File file = temporaryFolder.newFile("test.bin");
LibaioFile fileDescriptor = control.openFile(file, true);
fileDescriptor.lock();
fileDescriptor.close();
}
@Test
public void testAlloc() throws Exception
{
File file = temporaryFolder.newFile("test.bin");
LibaioFile fileDescriptor = control.openFile(file, true);
fileDescriptor.fill(10 * 1024 * 1024);
fileDescriptor.close();
}
@Test
public void testReleaseNullBuffer() throws Exception
{
boolean failed = false;
try
{
LibaioContext.freeBuffer(null);
}
catch (Exception expected)
{
failed = true;
}
Assert.assertTrue("Exception happened!", failed);
}
@Test
public void testMemset() throws Exception
{
ByteBuffer buffer = LibaioContext.newAlignedBuffer(512 * 8, 512);
for (int i = 0; i < buffer.capacity(); i++)
{
buffer.put((byte) 'z');
}
buffer.position(0);
for (int i = 0; i < buffer.capacity(); i++)
{
Assert.assertEquals((byte) 'z', buffer.get());
}
control.memsetBuffer(buffer);
buffer.position(0);
for (int i = 0; i < buffer.capacity(); i++)
{
Assert.assertEquals((byte) 0, buffer.get());
}
LibaioContext.freeBuffer(buffer);
}
@Test
public void testIOExceptionConditions() throws Exception
{
boolean exceptionThrown = false;
control.close();
control = new LibaioContext<>(LIBAIO_QUEUE_SIZE, false);
try
{
// There is no space for a queue this huge, the native layer should throw the exception
LibaioContext newController = new LibaioContext(Integer.MAX_VALUE, false);
}
catch (RuntimeException e)
{
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
exceptionThrown = false;
try
{
// this should throw an exception, we shouldn't be able to open a directory!
control.openFile(temporaryFolder.getRoot(), true);
}
catch (IOException expected)
{
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
exceptionThrown = false;
LibaioFile fileDescriptor = control.openFile(temporaryFolder.newFile(), true);
fileDescriptor.close();
try
{
fileDescriptor.close();
}
catch (IOException expected)
{
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
fileDescriptor = control.openFile(temporaryFolder.newFile(), true);
ByteBuffer buffer = fileDescriptor.newBuffer(512);
try
{
for (int i = 0; i < 512; i++)
{
buffer.put((byte) 'a');
}
for (int i = 0; i < LIBAIO_QUEUE_SIZE; i++)
{
fileDescriptor.write(i * 512, 512, buffer, new TestInfo());
}
boolean ex = false;
try
{
fileDescriptor.write(0, 512, buffer, new TestInfo());
}
catch (Exception e)
{
ex = true;
}
Assert.assertTrue(ex);
TestInfo[] callbacks = new TestInfo[LIBAIO_QUEUE_SIZE];
Assert.assertEquals(LIBAIO_QUEUE_SIZE, control.poll(callbacks, LIBAIO_QUEUE_SIZE, LIBAIO_QUEUE_SIZE));
// it should be possible to write now after queue space being released
fileDescriptor.write(0, 512, buffer, new TestInfo());
Assert.assertEquals(1, control.poll(callbacks, 1, 100));
TestInfo errorCallback = new TestInfo();
// odd positions will have failures through O_DIRECT
fileDescriptor.read(3, 512, buffer, errorCallback);
Assert.assertEquals(1, control.poll(callbacks, 1, 50));
Assert.assertTrue(callbacks[0].isError());
Assert.assertSame(errorCallback, (callbacks[0]));
// to help GC and the checkLeaks
callbacks = null;
errorCallback = null;
TestInfo.checkLeaks();
exceptionThrown = false;
try
{
LibaioContext.newAlignedBuffer(300, 512);
}
catch (RuntimeException e)
{
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
exceptionThrown = false;
try
{
LibaioContext.newAlignedBuffer(-512, 512);
}
catch (RuntimeException e)
{
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
}
finally
{
LibaioContext.freeBuffer(buffer);
}
}
@Test
public void testBlockedCallback() throws Exception
{
final LibaioContext blockedContext = new LibaioContext(500, true);
Thread t = new Thread()
{
public void run()
{
blockedContext.poll();
}
};
t.start();
int NUMBER_OF_BLOCKS = 5000;
final CountDownLatch latch = new CountDownLatch(NUMBER_OF_BLOCKS);
File file = temporaryFolder.newFile("sub-file.txt");
LibaioFile aioFile = blockedContext.openFile(file, true);
aioFile.fill(NUMBER_OF_BLOCKS * 512);
final AtomicInteger errors = new AtomicInteger(0);
class MyCallback implements SubmitInfo
{
@Override
public void onError(int errno, String message)
{
errors.incrementAndGet();
}
@Override
public void done()
{
latch.countDown();
}
}
MyCallback callback = new MyCallback();
ByteBuffer buffer = LibaioContext.newAlignedBuffer(512, 512);
for (int i = 0; i < 512; i++)
{
buffer.put((byte)'a');
}
long start = System.currentTimeMillis();
for (int i = 0; i < NUMBER_OF_BLOCKS; i++)
{
aioFile.write(i * 512, 512, buffer, callback);
}
long end = System.currentTimeMillis();
latch.await();
System.out.println("time = " + (end - start) + " writes/second=" + NUMBER_OF_BLOCKS * 1000L / (end - start));
//
// MultiThreadAsynchronousFileTest.debug((sync ? "Sync result:" : "Async result:") + " Records/Second = " +
// MultiThreadAsynchronousFileTest.NUMBER_OF_THREADS *
// MultiThreadAsynchronousFileTest.NUMBER_OF_LINES *
// 1000 /
// (endTime - startTime) +
// " total time = " +
// (endTime - startTime) +
// " total number of records = " +
// MultiThreadAsynchronousFileTest.NUMBER_OF_THREADS *
// MultiThreadAsynchronousFileTest.NUMBER_OF_LINES);
Thread.sleep(100);
blockedContext.close();
t.join();
}
private void fillupFile(File file, int blocks) throws IOException
{
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] bufferWrite = new byte[512];
for (int i = 0; i < 512; i++)
{
bufferWrite[i] = (byte) 0;
}
for (int i = 0; i < blocks; i++)
{
fileOutputStream.write(bufferWrite);
}
fileOutputStream.close();
}
static class TestInfo implements SubmitInfo
{
static AtomicInteger count = new AtomicInteger();
@Override
protected void finalize() throws Throwable
{
super.finalize();
count.decrementAndGet();
}
public static void checkLeaks() throws InterruptedException
{
for (int i = 0; count.get() != 0 && i < 50; i++)
{
WeakReference reference = new WeakReference(new Object());
while (reference.get() != null)
{
System.gc();
Thread.sleep(100);
}
}
Assert.assertEquals(0, count.get());
}
boolean error = false;
String errorMessage;
int errno;
public TestInfo()
{
count.incrementAndGet();
}
@Override
public void onError(int errno, String message)
{
this.errno = errno;
this.errorMessage = message;
this.error = true;
}
@Override
public void done()
{
}
public int getErrno()
{
return errno;
}
public void setErrno(int errno)
{
this.errno = errno;
}
public boolean isError()
{
return error;
}
public void setError(boolean error)
{
this.error = error;
}
public String getErrorMessage()
{
return errorMessage;
}
public void setErrorMessage(String errorMessage)
{
this.errorMessage = errorMessage;
}
}
}

View File

@ -20,6 +20,7 @@ package org.apache.activemq.artemis.core.protocol.proton.plug;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.qpid.proton.amqp.Binary; import org.apache.qpid.proton.amqp.Binary;
import org.apache.qpid.proton.amqp.transport.AmqpError; import org.apache.qpid.proton.amqp.transport.AmqpError;
import org.apache.qpid.proton.amqp.transport.ErrorCondition; import org.apache.qpid.proton.amqp.transport.ErrorCondition;
@ -30,7 +31,6 @@ import org.apache.qpid.proton.jms.EncodedMessage;
import org.apache.qpid.proton.message.ProtonJMessage; import org.apache.qpid.proton.message.ProtonJMessage;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.protocol.proton.ProtonProtocolManager; import org.apache.activemq.artemis.core.protocol.proton.ProtonProtocolManager;
import org.apache.activemq.artemis.core.server.QueueQueryResult; import org.apache.activemq.artemis.core.server.QueueQueryResult;
import org.apache.activemq.artemis.core.server.ServerConsumer; import org.apache.activemq.artemis.core.server.ServerConsumer;
@ -275,7 +275,7 @@ public class ProtonSessionIntegrationCallback implements AMQPSessionCallback, Se
serverSession.send(message, false); serverSession.send(message, false);
manager.getServer().getStorageManager().afterCompleteOperations(new IOAsyncTask() manager.getServer().getStorageManager().afterCompleteOperations(new IOCallback()
{ {
@Override @Override
public void done() public void done()

View File

@ -22,7 +22,7 @@ import io.netty.buffer.EmptyByteBuf;
import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttMessageType;
import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.server.ServerConsumer; import org.apache.activemq.artemis.core.server.ServerConsumer;
import org.apache.activemq.artemis.core.server.ServerMessage; import org.apache.activemq.artemis.core.server.ServerMessage;
import org.apache.activemq.artemis.core.server.impl.ServerMessageImpl; import org.apache.activemq.artemis.core.server.impl.ServerMessageImpl;
@ -183,7 +183,7 @@ public class MQTTPublishManager
private void createMessageAck(final int messageId, final int qos) private void createMessageAck(final int messageId, final int qos)
{ {
session.getServer().getStorageManager().afterCompleteOperations(new IOAsyncTask() session.getServer().getStorageManager().afterCompleteOperations(new IOCallback()
{ {
@Override @Override
public void done() public void done()

View File

@ -36,6 +36,7 @@ import org.apache.activemq.artemis.api.core.BaseInterceptor;
import org.apache.activemq.artemis.api.core.Interceptor; import org.apache.activemq.artemis.api.core.Interceptor;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.management.CoreNotificationType; import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.api.core.management.ManagementHelper; import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQConsumer; import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQConsumer;
import org.apache.activemq.artemis.core.server.management.ManagementService; import org.apache.activemq.artemis.core.server.management.ManagementService;
@ -62,7 +63,6 @@ import org.apache.activemq.command.TransactionId;
import org.apache.activemq.command.TransactionInfo; import org.apache.activemq.command.TransactionInfo;
import org.apache.activemq.command.WireFormatInfo; import org.apache.activemq.command.WireFormatInfo;
import org.apache.activemq.command.XATransactionId; import org.apache.activemq.command.XATransactionId;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQConnectionContext; import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQConnectionContext;
import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQPersistenceAdapter; import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQPersistenceAdapter;
import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQProducerBrokerExchange; import org.apache.activemq.artemis.core.protocol.openwire.amq.AMQProducerBrokerExchange;
@ -261,7 +261,7 @@ public class OpenWireProtocolManager implements ProtocolManager<Interceptor>, No
public void sendReply(final OpenWireConnection connection, public void sendReply(final OpenWireConnection connection,
final Command command) final Command command)
{ {
server.getStorageManager().afterCompleteOperations(new IOAsyncTask() server.getStorageManager().afterCompleteOperations(new IOCallback()
{ {
public void onError(final int errorCode, final String errorMessage) public void onError(final int errorCode, final String errorMessage)
{ {

View File

@ -33,7 +33,7 @@ import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient; import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.management.CoreNotificationType; import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
import org.apache.activemq.artemis.api.core.management.ManagementHelper; import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.postoffice.BindingType; import org.apache.activemq.artemis.core.postoffice.BindingType;
import org.apache.activemq.artemis.core.remoting.impl.netty.NettyServerConnection; import org.apache.activemq.artemis.core.remoting.impl.netty.NettyServerConnection;
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants; import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
@ -357,7 +357,7 @@ class StompProtocolManager implements ProtocolManager<StompFrameInterceptor>, No
public void sendReply(final StompConnection connection, final StompFrame frame) public void sendReply(final StompConnection connection, final StompFrame frame)
{ {
server.getStorageManager().afterCompleteOperations(new IOAsyncTask() server.getStorageManager().afterCompleteOperations(new IOCallback()
{ {
public void onError(final int errorCode, final String errorMessage) public void onError(final int errorCode, final String errorMessage)
{ {

View File

@ -41,7 +41,7 @@ import org.apache.activemq.artemis.core.config.ha.SharedStoreMasterPolicyConfigu
import org.apache.activemq.artemis.core.config.ha.SharedStoreSlavePolicyConfiguration; import org.apache.activemq.artemis.core.config.ha.SharedStoreSlavePolicyConfiguration;
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.config.impl.Validators; import org.apache.activemq.artemis.core.config.impl.Validators;
import org.apache.activemq.artemis.core.journal.impl.AIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.aio.AIOSequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.JournalConstants; import org.apache.activemq.artemis.core.journal.impl.JournalConstants;
import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;

View File

@ -26,7 +26,7 @@ import org.apache.activemq.artemis.core.settings.HierarchicalRepositoryChangeLis
* <PRE> * <PRE>
* *
* +--------------+ 1 +----------------+ N +--------------+ N +--------+ 1 +-------------------+ * +--------------+ 1 +----------------+ N +--------------+ N +--------+ 1 +-------------------+
* | {@link org.apache.activemq.artemis.core.postoffice.PostOffice} |-------&gt; |{@link PagingManager}|-------&gt; |{@link PagingStore} | ------&gt; | {@link org.apache.activemq.artemis.core.paging.impl.Page} | ------&gt; | {@link org.apache.activemq.artemis.core.journal.SequentialFile} | * | {@link org.apache.activemq.artemis.core.postoffice.PostOffice} |-------&gt; |{@link PagingManager}|-------&gt; |{@link PagingStore} | ------&gt; | {@link org.apache.activemq.artemis.core.paging.impl.Page} | ------&gt; | {@link SequentialFile} |
* +--------------+ +----------------+ +--------------+ +--------+ +-------------------+ * +--------------+ +----------------+ +--------------+ +--------+ +-------------------+
* | 1 ^ * | 1 ^
* | | * | |

View File

@ -158,7 +158,7 @@ public interface PagingStore extends ActiveMQComponent
/** /**
* Sends the pages with given IDs to the {@link ReplicationManager}. * Sends the pages with given IDs to the {@link ReplicationManager}.
* <p> * <p>
* Sending is done here to avoid exposing the internal {@link org.apache.activemq.artemis.core.journal.SequentialFile}s. * Sending is done here to avoid exposing the internal {@link SequentialFile}s.
* *
* @param replicator * @param replicator
* @param pageIds * @param pageIds

View File

@ -19,7 +19,7 @@ package org.apache.activemq.artemis.core.paging;
import java.util.List; import java.util.List;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.core.settings.impl.AddressSettings;

View File

@ -32,8 +32,8 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.filter.Filter; import org.apache.activemq.artemis.core.filter.Filter;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.paging.PageTransactionInfo; import org.apache.activemq.artemis.core.paging.PageTransactionInfo;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
import org.apache.activemq.artemis.core.paging.PagingStore; import org.apache.activemq.artemis.core.paging.PagingStore;
@ -521,7 +521,7 @@ final class PageSubscriptionImpl implements PageSubscription
store.storeCursorAcknowledge(cursorId, position); store.storeCursorAcknowledge(cursorId, position);
} }
store.afterCompleteOperations(new IOAsyncTask() store.afterCompleteOperations(new IOCallback()
{ {
volatile String error = ""; volatile String error = "";
@ -541,7 +541,7 @@ final class PageSubscriptionImpl implements PageSubscription
@Override @Override
public String toString() public String toString()
{ {
return IOAsyncTask.class.getSimpleName() + "(" + PageSubscriptionImpl.class.getSimpleName() + ") " + error; return IOCallback.class.getSimpleName() + "(" + PageSubscriptionImpl.class.getSimpleName() + ") " + error;
} }
}); });
} }

View File

@ -25,8 +25,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
import org.apache.activemq.artemis.core.paging.cursor.LivePageCache; import org.apache.activemq.artemis.core.paging.cursor.LivePageCache;
import org.apache.activemq.artemis.core.paging.cursor.PageSubscriptionCounter; import org.apache.activemq.artemis.core.paging.cursor.PageSubscriptionCounter;

View File

@ -29,9 +29,9 @@ import java.util.List;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.journal.IOCriticalErrorListener; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.core.paging.PagingManager; import org.apache.activemq.artemis.core.paging.PagingManager;
import org.apache.activemq.artemis.core.paging.PagingStore; import org.apache.activemq.artemis.core.paging.PagingStore;
import org.apache.activemq.artemis.core.paging.PagingStoreFactory; import org.apache.activemq.artemis.core.paging.PagingStoreFactory;
@ -208,6 +208,6 @@ public class PagingStoreFactoryNIO implements PagingStoreFactory
private SequentialFileFactory newFileFactory(final String directoryName) private SequentialFileFactory newFileFactory(final String directoryName)
{ {
return new NIOSequentialFileFactory(new File(directory, directoryName), false, critialErrorListener); return new NIOSequentialFileFactory(new File(directory, directoryName), false, critialErrorListener, 1);
} }
} }

View File

@ -37,8 +37,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.paging.PageTransactionInfo; import org.apache.activemq.artemis.core.paging.PageTransactionInfo;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
import org.apache.activemq.artemis.core.paging.PagingManager; import org.apache.activemq.artemis.core.paging.PagingManager;
@ -576,7 +576,7 @@ public class PagingStoreImpl implements PagingStore
public boolean checkPageFileExists(final int pageNumber) public boolean checkPageFileExists(final int pageNumber)
{ {
String fileName = createFileName(pageNumber); String fileName = createFileName(pageNumber);
SequentialFile file = fileFactory.createSequentialFile(fileName, 1); SequentialFile file = fileFactory.createSequentialFile(fileName);
return file.exists(); return file.exists();
} }
@ -589,7 +589,7 @@ public class PagingStoreImpl implements PagingStore
fileFactory = storeFactory.newFileFactory(getStoreName()); fileFactory = storeFactory.newFileFactory(getStoreName());
} }
SequentialFile file = fileFactory.createSequentialFile(fileName, 1000); SequentialFile file = fileFactory.createSequentialFile(fileName);
Page page = new Page(storeName, storageManager, fileFactory, file, pageNumber); Page page = new Page(storeName, storageManager, fileFactory, file, pageNumber);
@ -1227,7 +1227,7 @@ public class PagingStoreImpl implements PagingStore
{ {
for (Integer id : pageIds) for (Integer id : pageIds)
{ {
SequentialFile sFile = fileFactory.createSequentialFile(createFileName(id), 1); SequentialFile sFile = fileFactory.createSequentialFile(createFileName(id));
if (!sFile.exists()) if (!sFile.exists())
{ {
continue; continue;

View File

@ -16,7 +16,7 @@
*/ */
package org.apache.activemq.artemis.core.persistence; package org.apache.activemq.artemis.core.persistence;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.IOCompletion; import org.apache.activemq.artemis.core.journal.IOCompletion;
/** /**
@ -28,7 +28,7 @@ public interface OperationContext extends IOCompletion
{ {
/** Execute the task when all IO operations are complete, /** Execute the task when all IO operations are complete,
* Or execute it immediately if nothing is pending. */ * Or execute it immediately if nothing is pending. */
void executeOnCompletion(IOAsyncTask runnable); void executeOnCompletion(IOCallback runnable);
void replicationLineUp(); void replicationLineUp();

View File

@ -25,10 +25,10 @@ import java.util.concurrent.Executor;
import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.Journal;
import org.apache.activemq.artemis.core.journal.JournalLoadInformation; import org.apache.activemq.artemis.core.journal.JournalLoadInformation;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.message.impl.MessageInternal; import org.apache.activemq.artemis.core.message.impl.MessageInternal;
import org.apache.activemq.artemis.core.paging.PageTransactionInfo; import org.apache.activemq.artemis.core.paging.PageTransactionInfo;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
@ -99,7 +99,7 @@ public interface StorageManager extends IDGenerator, ActiveMQComponent
void pageWrite(PagedMessage message, int pageNumber); void pageWrite(PagedMessage message, int pageNumber);
void afterCompleteOperations(IOAsyncTask run); void afterCompleteOperations(IOCallback run);
/** /**
* Block until the operations are done. * Block until the operations are done.

View File

@ -31,12 +31,12 @@ import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.core.journal.EncodingSupport;
import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.TransactionFailureCallback; import org.apache.activemq.artemis.core.journal.TransactionFailureCallback;
import org.apache.activemq.artemis.core.journal.impl.JournalFile; import org.apache.activemq.artemis.core.journal.impl.JournalFile;
import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
import org.apache.activemq.artemis.core.journal.impl.JournalReaderCallback; import org.apache.activemq.artemis.core.journal.impl.JournalReaderCallback;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.core.paging.cursor.impl.PageSubscriptionCounterImpl; import org.apache.activemq.artemis.core.paging.cursor.impl.PageSubscriptionCounterImpl;
import org.apache.activemq.artemis.core.paging.impl.PageTransactionInfoImpl; import org.apache.activemq.artemis.core.paging.impl.PageTransactionInfoImpl;
import org.apache.activemq.artemis.core.persistence.impl.journal.BatchingIDGenerator.IDCounterEncoding; import org.apache.activemq.artemis.core.persistence.impl.journal.BatchingIDGenerator.IDCounterEncoding;
@ -109,7 +109,7 @@ public final class DescribeJournal
public static void describeBindingsJournal(final File bindingsDir) throws Exception public static void describeBindingsJournal(final File bindingsDir) throws Exception
{ {
SequentialFileFactory bindingsFF = new NIOSequentialFileFactory(bindingsDir, null); SequentialFileFactory bindingsFF = new NIOSequentialFileFactory(bindingsDir, null, 1);
JournalImpl bindings = new JournalImpl(1024 * 1024, 2, -1, 0, bindingsFF, "activemq-bindings", "bindings", 1); JournalImpl bindings = new JournalImpl(1024 * 1024, 2, -1, 0, bindingsFF, "activemq-bindings", "bindings", 1);
describeJournal(bindingsFF, bindings, bindingsDir); describeJournal(bindingsFF, bindings, bindingsDir);
@ -118,7 +118,7 @@ public final class DescribeJournal
public static DescribeJournal describeMessagesJournal(final File messagesDir) throws Exception public static DescribeJournal describeMessagesJournal(final File messagesDir) throws Exception
{ {
SequentialFileFactory messagesFF = new NIOSequentialFileFactory(messagesDir, null); SequentialFileFactory messagesFF = new NIOSequentialFileFactory(messagesDir, null, 1);
// Will use only default values. The load function should adapt to anything different // Will use only default values. The load function should adapt to anything different
ConfigurationImpl defaultValues = new ConfigurationImpl(); ConfigurationImpl defaultValues = new ConfigurationImpl();

View File

@ -53,22 +53,22 @@ import org.apache.activemq.artemis.api.core.ActiveMQInternalErrorException;
import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.filter.Filter; import org.apache.activemq.artemis.core.filter.Filter;
import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.core.journal.EncodingSupport;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener;
import org.apache.activemq.artemis.core.journal.IOCriticalErrorListener;
import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.Journal;
import org.apache.activemq.artemis.core.journal.JournalLoadInformation; import org.apache.activemq.artemis.core.journal.JournalLoadInformation;
import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.TransactionFailureCallback; import org.apache.activemq.artemis.core.journal.TransactionFailureCallback;
import org.apache.activemq.artemis.core.journal.impl.AIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.aio.AIOSequentialFileFactory;
import org.apache.activemq.artemis.core.journal.impl.JournalFile; import org.apache.activemq.artemis.core.journal.impl.JournalFile;
import org.apache.activemq.artemis.core.journal.impl.JournalImpl; import org.apache.activemq.artemis.core.journal.impl.JournalImpl;
import org.apache.activemq.artemis.core.journal.impl.NIOSequentialFileFactory; import org.apache.activemq.artemis.core.io.nio.NIOSequentialFileFactory;
import org.apache.activemq.artemis.core.message.impl.MessageInternal; import org.apache.activemq.artemis.core.message.impl.MessageInternal;
import org.apache.activemq.artemis.core.paging.PageTransactionInfo; import org.apache.activemq.artemis.core.paging.PageTransactionInfo;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
@ -236,7 +236,9 @@ public class JournalStorageManager implements StorageManager
} }
SequentialFileFactory bindingsFF = new NIOSequentialFileFactory(config.getBindingsLocation(), criticalErrorListener); SequentialFileFactory bindingsFF = new NIOSequentialFileFactory(config.getBindingsLocation(),
criticalErrorListener,
config.getJournalMaxIO_NIO());
Journal localBindings = new JournalImpl(1024 * 1024, Journal localBindings = new JournalImpl(1024 * 1024,
2, 2,
@ -261,6 +263,7 @@ public class JournalStorageManager implements StorageManager
journalFF = new AIOSequentialFileFactory(config.getJournalLocation(), journalFF = new AIOSequentialFileFactory(config.getJournalLocation(),
config.getJournalBufferSize_AIO(), config.getJournalBufferSize_AIO(),
config.getJournalBufferTimeout_AIO(), config.getJournalBufferTimeout_AIO(),
config.getJournalMaxIO_AIO(),
config.isLogJournalWriteRate(), config.isLogJournalWriteRate(),
criticalErrorListener); criticalErrorListener);
} }
@ -271,6 +274,7 @@ public class JournalStorageManager implements StorageManager
true, true,
config.getJournalBufferSize_NIO(), config.getJournalBufferSize_NIO(),
config.getJournalBufferTimeout_NIO(), config.getJournalBufferTimeout_NIO(),
config.getJournalMaxIO_NIO(),
config.isLogJournalWriteRate(), config.isLogJournalWriteRate(),
criticalErrorListener); criticalErrorListener);
} }
@ -296,7 +300,8 @@ public class JournalStorageManager implements StorageManager
largeMessagesDirectory = config.getLargeMessagesDirectory(); largeMessagesDirectory = config.getLargeMessagesDirectory();
largeMessagesFactory = new NIOSequentialFileFactory(config.getLargeMessagesLocation(), false, criticalErrorListener); largeMessagesFactory = new NIOSequentialFileFactory(config.getLargeMessagesLocation(), false, criticalErrorListener,
1);
perfBlastPages = config.getJournalPerfBlastPages(); perfBlastPages = config.getJournalPerfBlastPages();
@ -572,7 +577,7 @@ public class JournalStorageManager implements StorageManager
String fileName = entry.getValue().getA(); String fileName = entry.getValue().getA();
final long id = entry.getKey(); final long id = entry.getKey();
long size = entry.getValue().getB(); long size = entry.getValue().getB();
SequentialFile seqFile = largeMessagesFactory.createSequentialFile(fileName, 1); SequentialFile seqFile = largeMessagesFactory.createSequentialFile(fileName);
if (!seqFile.exists()) if (!seqFile.exists())
continue; continue;
replicator.syncLargeMessageFile(seqFile, size, id); replicator.syncLargeMessageFile(seqFile, size, id);
@ -608,7 +613,7 @@ public class JournalStorageManager implements StorageManager
if (!largeMessagesToDelete.contains(id)) if (!largeMessagesToDelete.contains(id))
{ {
idList.add(id); idList.add(id);
SequentialFile seqFile = largeMessagesFactory.createSequentialFile(filename, 1); SequentialFile seqFile = largeMessagesFactory.createSequentialFile(filename);
long size = seqFile.size(); long size = seqFile.size();
largeMessages.put(id, new Pair<String, Long>(filename, size)); largeMessages.put(id, new Pair<String, Long>(filename, size));
} }
@ -747,7 +752,7 @@ public class JournalStorageManager implements StorageManager
return new OperationContextImpl(executor1); return new OperationContextImpl(executor1);
} }
public void afterCompleteOperations(final IOAsyncTask run) public void afterCompleteOperations(final IOCallback run)
{ {
getContext().executeOnCompletion(run); getContext().executeOnCompletion(run);
} }
@ -2498,7 +2503,7 @@ public class JournalStorageManager implements StorageManager
public SequentialFile createFileForLargeMessage(final long messageID, LargeMessageExtension extension) public SequentialFile createFileForLargeMessage(final long messageID, LargeMessageExtension extension)
{ {
return largeMessagesFactory.createSequentialFile(messageID + extension.getExtension(), -1); return largeMessagesFactory.createSequentialFile(messageID + extension.getExtension());
} }
@ -2788,7 +2793,7 @@ public class JournalStorageManager implements StorageManager
List<String> tmpFiles = largeMessagesFactory.listFiles("tmp"); List<String> tmpFiles = largeMessagesFactory.listFiles("tmp");
for (String tmpFile : tmpFiles) for (String tmpFile : tmpFiles)
{ {
SequentialFile file = largeMessagesFactory.createSequentialFile(tmpFile, -1); SequentialFile file = largeMessagesFactory.createSequentialFile(tmpFile);
file.delete(); file.delete();
} }
} }
@ -2830,7 +2835,7 @@ public class JournalStorageManager implements StorageManager
return DummyOperationContext.instance; return DummyOperationContext.instance;
} }
public void executeOnCompletion(final IOAsyncTask runnable) public void executeOnCompletion(final IOCallback runnable)
{ {
// There are no executeOnCompletion calls while using the DummyOperationContext // There are no executeOnCompletion calls while using the DummyOperationContext
// However we keep the code here for correctness // However we keep the code here for correctness

View File

@ -24,7 +24,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
import org.apache.activemq.artemis.api.core.ActiveMQInternalErrorException; import org.apache.activemq.artemis.api.core.ActiveMQInternalErrorException;
import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.message.BodyEncoder; import org.apache.activemq.artemis.core.message.BodyEncoder;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.LargeServerMessage; import org.apache.activemq.artemis.core.server.LargeServerMessage;

View File

@ -20,7 +20,7 @@ import java.nio.ByteBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.persistence.StorageManager; import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.persistence.StorageManager.LargeMessageExtension; import org.apache.activemq.artemis.core.persistence.StorageManager.LargeMessageExtension;
import org.apache.activemq.artemis.core.replication.ReplicatedLargeMessage; import org.apache.activemq.artemis.core.replication.ReplicatedLargeMessage;
@ -62,7 +62,7 @@ public final class LargeServerMessageInSync implements ReplicatedLargeMessage
buffer.rewind(); buffer.rewind();
int bytesRead = appendFile.read(buffer); int bytesRead = appendFile.read(buffer);
if (bytesRead > 0) if (bytesRead > 0)
mainSeqFile.writeInternal(buffer); mainSeqFile.writeDirect(buffer, false);
if (bytesRead < buffer.capacity()) if (bytesRead < buffer.capacity())
{ {
break; break;

View File

@ -25,7 +25,7 @@ import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.impl.SimpleWaitIOCallback; import org.apache.activemq.artemis.core.journal.impl.SimpleWaitIOCallback;
import org.apache.activemq.artemis.core.persistence.OperationContext; import org.apache.activemq.artemis.core.persistence.OperationContext;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
@ -134,7 +134,7 @@ public class OperationContextImpl implements OperationContext
checkTasks(); checkTasks();
} }
public void executeOnCompletion(final IOAsyncTask completion) public void executeOnCompletion(final IOCallback completion)
{ {
if (errorCode != -1) if (errorCode != -1)
{ {
@ -219,7 +219,7 @@ public class OperationContextImpl implements OperationContext
/** /**
* @param task * @param task
*/ */
private void execute(final IOAsyncTask task) private void execute(final IOCallback task)
{ {
executorsPending.incrementAndGet(); executorsPending.incrementAndGet();
try try
@ -243,7 +243,7 @@ public class OperationContextImpl implements OperationContext
} }
catch (Throwable e) catch (Throwable e)
{ {
ActiveMQServerLogger.LOGGER.errorExecutingIOAsyncTask(e); ActiveMQServerLogger.LOGGER.errorExecutingAIOCallback(e);
executorsPending.decrementAndGet(); executorsPending.decrementAndGet();
task.onError(ActiveMQExceptionType.INTERNAL_ERROR.getCode(), task.onError(ActiveMQExceptionType.INTERNAL_ERROR.getCode(),
"It wasn't possible to complete IO operation - " + e.getMessage()); "It wasn't possible to complete IO operation - " + e.getMessage());
@ -296,9 +296,9 @@ public class OperationContextImpl implements OperationContext
final int replicationLined; final int replicationLined;
final int pageLined; final int pageLined;
final IOAsyncTask task; final IOCallback task;
TaskHolder(final IOAsyncTask task) TaskHolder(final IOCallback task)
{ {
storeLined = storeLineUp.intValue(); storeLined = storeLineUp.intValue();
replicationLined = replicationLineUp.intValue(); replicationLined = replicationLineUp.intValue();

View File

@ -17,7 +17,7 @@
package org.apache.activemq.artemis.core.persistence.impl.nullpm; package org.apache.activemq.artemis.core.persistence.impl.nullpm;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.server.LargeServerMessage; import org.apache.activemq.artemis.core.server.LargeServerMessage;
import org.apache.activemq.artemis.core.server.ServerMessage; import org.apache.activemq.artemis.core.server.ServerMessage;
import org.apache.activemq.artemis.core.server.impl.ServerMessageImpl; import org.apache.activemq.artemis.core.server.impl.ServerMessageImpl;

View File

@ -27,10 +27,10 @@ import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.artemis.api.core.Pair; import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.journal.IOAsyncTask; import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.Journal;
import org.apache.activemq.artemis.core.journal.JournalLoadInformation; import org.apache.activemq.artemis.core.journal.JournalLoadInformation;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.message.impl.MessageInternal; import org.apache.activemq.artemis.core.message.impl.MessageInternal;
import org.apache.activemq.artemis.core.paging.PageTransactionInfo; import org.apache.activemq.artemis.core.paging.PageTransactionInfo;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
@ -112,7 +112,7 @@ public class NullStorageManager implements StorageManager
} }
@Override @Override
public void executeOnCompletion(final IOAsyncTask runnable) public void executeOnCompletion(final IOCallback runnable)
{ {
runnable.done(); runnable.done();
} }
@ -359,7 +359,7 @@ public class NullStorageManager implements StorageManager
} }
@Override @Override
public void afterCompleteOperations(final IOAsyncTask run) public void afterCompleteOperations(final IOCallback run)
{ {
run.done(); run.done();
} }

View File

@ -41,8 +41,8 @@ import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.management.CoreNotificationType; import org.apache.activemq.artemis.api.core.management.CoreNotificationType;
import org.apache.activemq.artemis.api.core.management.ManagementHelper; import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.api.core.management.NotificationType; import org.apache.activemq.artemis.api.core.management.NotificationType;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.filter.Filter; import org.apache.activemq.artemis.core.filter.Filter;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.message.impl.MessageImpl; import org.apache.activemq.artemis.core.message.impl.MessageImpl;
import org.apache.activemq.artemis.core.paging.PagingManager; import org.apache.activemq.artemis.core.paging.PagingManager;
import org.apache.activemq.artemis.core.paging.PagingStore; import org.apache.activemq.artemis.core.paging.PagingStore;
@ -1179,7 +1179,7 @@ public class PostOfficeImpl implements PostOffice, NotificationListener, Binding
{ {
// This will use the same thread if there are no pending operations // This will use the same thread if there are no pending operations
// avoiding a context switch on this case // avoiding a context switch on this case
storageManager.afterCompleteOperations(new IOAsyncTask() storageManager.afterCompleteOperations(new IOCallback()
{ {
public void onError(final int errorCode, final String errorMessage) public void onError(final int errorCode, final String errorMessage)
{ {
@ -1249,7 +1249,7 @@ public class PostOfficeImpl implements PostOffice, NotificationListener, Binding
queues.addAll(durableQueues); queues.addAll(durableQueues);
queues.addAll(nonDurableQueues); queues.addAll(nonDurableQueues);
storageManager.afterCompleteOperations(new IOAsyncTask() storageManager.afterCompleteOperations(new IOCallback()
{ {
public void onError(int errorCode, String errorMessage) public void onError(int errorCode, String errorMessage)

View File

@ -58,8 +58,8 @@ import javax.transaction.xa.Xid;
import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.ActiveMQExceptionType; import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
import org.apache.activemq.artemis.api.core.ActiveMQInternalErrorException; import org.apache.activemq.artemis.api.core.ActiveMQInternalErrorException;
import org.apache.activemq.artemis.core.io.IOCallback;
import org.apache.activemq.artemis.core.exception.ActiveMQXAException; import org.apache.activemq.artemis.core.exception.ActiveMQXAException;
import org.apache.activemq.artemis.core.journal.IOAsyncTask;
import org.apache.activemq.artemis.core.persistence.StorageManager; import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl; import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateQueueMessage; import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.CreateQueueMessage;
@ -614,7 +614,7 @@ public class ServerSessionPacketHandler implements ChannelHandler
final boolean flush, final boolean flush,
final boolean closeChannel) final boolean closeChannel)
{ {
storageManager.afterCompleteOperations(new IOAsyncTask() storageManager.afterCompleteOperations(new IOCallback()
{ {
public void onError(final int errorCode, final String errorMessage) public void onError(final int errorCode, final String errorMessage)
{ {

View File

@ -26,7 +26,7 @@ import org.apache.activemq.artemis.core.journal.JournalLoadInformation;
import org.apache.activemq.artemis.core.journal.LoaderCallback; import org.apache.activemq.artemis.core.journal.LoaderCallback;
import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo;
import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.RecordInfo;
import org.apache.activemq.artemis.core.journal.SequentialFileFactory; import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.TransactionFailureCallback; import org.apache.activemq.artemis.core.journal.TransactionFailureCallback;
import org.apache.activemq.artemis.core.journal.impl.JournalFile; import org.apache.activemq.artemis.core.journal.impl.JournalFile;
import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding; import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding;

View File

@ -34,11 +34,11 @@ import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.journal.IOCriticalErrorListener; import org.apache.activemq.artemis.core.io.IOCriticalErrorListener;
import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.Journal;
import org.apache.activemq.artemis.core.journal.Journal.JournalState; import org.apache.activemq.artemis.core.journal.Journal.JournalState;
import org.apache.activemq.artemis.core.journal.JournalLoadInformation; import org.apache.activemq.artemis.core.journal.JournalLoadInformation;
import org.apache.activemq.artemis.core.journal.SequentialFile; import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.journal.impl.FileWrapperJournal; import org.apache.activemq.artemis.core.journal.impl.FileWrapperJournal;
import org.apache.activemq.artemis.core.journal.impl.JournalFile; import org.apache.activemq.artemis.core.journal.impl.JournalFile;
import org.apache.activemq.artemis.core.paging.PagedMessage; import org.apache.activemq.artemis.core.paging.PagedMessage;
@ -546,7 +546,7 @@ public final class ReplicationEndpoint implements ChannelHandler, ActiveMQCompon
if (!channel1.isOpen()) if (!channel1.isOpen())
{ {
channel1.open(1, false); channel1.open();
} }
channel1.writeDirect(ByteBuffer.wrap(data), true); channel1.writeDirect(ByteBuffer.wrap(data), true);
} }

Some files were not shown because too many files have changed in this diff Show More