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:
parent
661f695ee2
commit
6fe9e0ebd6
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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. -->
|
||||||
|
|
|
@ -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>
|
|
@ -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());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
|
@ -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();
|
||||||
|
|
|
@ -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.
|
|
@ -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);
|
|
@ -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
|
||||||
{
|
{
|
|
@ -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;
|
|
@ -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.
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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())
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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>
|
||||||
|
|
|
@ -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()
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -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_*/
|
|
|
@ -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_*/
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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_*/
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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_*/
|
|
|
@ -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_*/
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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_*/
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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_*/
|
|
|
@ -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_*/
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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);
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
|
@ -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;
|
||||||
{
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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} |-------> |{@link PagingManager}|-------> |{@link PagingStore} | ------> | {@link org.apache.activemq.artemis.core.paging.impl.Page} | ------> | {@link org.apache.activemq.artemis.core.journal.SequentialFile} |
|
* | {@link org.apache.activemq.artemis.core.postoffice.PostOffice} |-------> |{@link PagingManager}|-------> |{@link PagingStore} | ------> | {@link org.apache.activemq.artemis.core.paging.impl.Page} | ------> | {@link SequentialFile} |
|
||||||
* +--------------+ +----------------+ +--------------+ +--------+ +-------------------+
|
* +--------------+ +----------------+ +--------------+ +--------+ +-------------------+
|
||||||
* | 1 ^
|
* | 1 ^
|
||||||
* | |
|
* | |
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue