mirror of
https://github.com/OpenHFT/Chronicle-Map.git
synced 2021-09-13 01:26:12 +00:00
Merge branch 'master' of github.com:OpenHFT/Chronicle-Map
This commit is contained in:
@@ -15,7 +15,8 @@
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses />.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>net.openhft</groupId>
|
||||
@@ -46,7 +47,7 @@
|
||||
<groupId>net.openhft</groupId>
|
||||
<artifactId>chronicle-bom</artifactId>
|
||||
<type>pom</type>
|
||||
<version>1.11.16</version>
|
||||
<version>1.12.10</version>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public final class SizePrefixedBlob {
|
||||
public static final int SELF_BOOTSTRAPPING_HEADER_OFFSET = 12;
|
||||
|
||||
public static final int READY = 0;
|
||||
public static final int NOT_READY = 0x80000000;
|
||||
public static final int NOT_COMPLETE = 0x80000000;
|
||||
|
||||
public static final int DATA = 0;
|
||||
public static final int META_DATA = 0x40000000;
|
||||
|
||||
@@ -135,6 +135,62 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
LoggerFactory.getLogger(ChronicleMapBuilder.class.getName());
|
||||
|
||||
private static final double UNDEFINED_DOUBLE_CONFIG = Double.NaN;
|
||||
private static final ConcurrentHashMap<File, Void> fileLockingControl =
|
||||
new ConcurrentHashMap<>(128);
|
||||
private static int MAX_BOOTSTRAPPING_HEADER_SIZE = (int) MemoryUnit.KILOBYTES.toBytes(16);
|
||||
SerializationBuilder<K> keyBuilder;
|
||||
SerializationBuilder<V> valueBuilder;
|
||||
K averageKey;
|
||||
V averageValue;
|
||||
/**
|
||||
* Default timeout is 1 minute. Even loopback tests converge often in the course of seconds,
|
||||
* let alone WAN replication over many nodes might take tens of seconds.
|
||||
* <p/>
|
||||
* TODO review
|
||||
*/
|
||||
long cleanupTimeout = 1;
|
||||
TimeUnit cleanupTimeoutUnit = TimeUnit.MINUTES;
|
||||
boolean cleanupRemovedEntries = true;
|
||||
|
||||
//////////////////////////////
|
||||
// Configuration fields
|
||||
DefaultValueProvider<K, V> defaultValueProvider = DefaultSpi.defaultValueProvider();
|
||||
byte replicationIdentifier = -1;
|
||||
MapMethods<K, V, ?> methods = DefaultSpi.mapMethods();
|
||||
MapEntryOperations<K, V, ?> entryOperations = mapEntryOperations();
|
||||
MapRemoteOperations<K, V, ?> remoteOperations = mapRemoteOperations();
|
||||
// not final because of cloning
|
||||
private ChronicleMapBuilderPrivateAPI<K, V> privateAPI =
|
||||
new ChronicleMapBuilderPrivateAPI<>(this);
|
||||
// used when configuring the number of segments.
|
||||
private int minSegments = -1;
|
||||
private int actualSegments = -1;
|
||||
// used when reading the number of entries per
|
||||
private long entriesPerSegment = -1L;
|
||||
private long actualChunksPerSegmentTier = -1L;
|
||||
private double averageKeySize = UNDEFINED_DOUBLE_CONFIG;
|
||||
private K sampleKey;
|
||||
private double averageValueSize = UNDEFINED_DOUBLE_CONFIG;
|
||||
private V sampleValue;
|
||||
private int actualChunkSize = 0;
|
||||
private int worstAlignment = -1;
|
||||
private int maxChunksPerEntry = -1;
|
||||
private int alignment = UNDEFINED_ALIGNMENT_CONFIG;
|
||||
private long entries = -1L;
|
||||
private double maxBloatFactor = 1.0;
|
||||
private boolean allowSegmentTiering = true;
|
||||
private double nonTieredSegmentsPercentile = 0.99999;
|
||||
private boolean aligned64BitMemoryOperationsAtomic = OS.is64Bit();
|
||||
private ChecksumEntries checksumEntries = ChecksumEntries.IF_PERSISTED;
|
||||
private boolean putReturnsNull = false;
|
||||
private boolean removeReturnsNull = false;
|
||||
private boolean replicated;
|
||||
private boolean persisted;
|
||||
|
||||
ChronicleMapBuilder(Class<K> keyClass, Class<V> valueClass) {
|
||||
keyBuilder = new SerializationBuilder<>(keyClass);
|
||||
valueBuilder = new SerializationBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
private static boolean isDefined(double config) {
|
||||
return !isNaN(config);
|
||||
@@ -155,14 +211,8 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
return (long) v;
|
||||
}
|
||||
|
||||
private static int MAX_BOOTSTRAPPING_HEADER_SIZE = (int) MemoryUnit.KILOBYTES.toBytes(16);
|
||||
|
||||
private static final ConcurrentHashMap<File, Void> fileLockingControl =
|
||||
new ConcurrentHashMap<>(128);
|
||||
|
||||
interface FileIOAction {
|
||||
void fileIOAction() throws IOException;
|
||||
}
|
||||
//////////////////////////////
|
||||
// Instance fields
|
||||
|
||||
/**
|
||||
* When Chronicle Maps are created using {@link #createPersistedTo(File)} or
|
||||
@@ -189,73 +239,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
});
|
||||
}
|
||||
|
||||
// not final because of cloning
|
||||
private ChronicleMapBuilderPrivateAPI<K, V> privateAPI =
|
||||
new ChronicleMapBuilderPrivateAPI<>(this);
|
||||
|
||||
//////////////////////////////
|
||||
// Configuration fields
|
||||
|
||||
SerializationBuilder<K> keyBuilder;
|
||||
SerializationBuilder<V> valueBuilder;
|
||||
|
||||
// used when configuring the number of segments.
|
||||
private int minSegments = -1;
|
||||
private int actualSegments = -1;
|
||||
// used when reading the number of entries per
|
||||
private long entriesPerSegment = -1L;
|
||||
private long actualChunksPerSegmentTier = -1L;
|
||||
private double averageKeySize = UNDEFINED_DOUBLE_CONFIG;
|
||||
K averageKey;
|
||||
private K sampleKey;
|
||||
private double averageValueSize = UNDEFINED_DOUBLE_CONFIG;
|
||||
V averageValue;
|
||||
private V sampleValue;
|
||||
private int actualChunkSize = 0;
|
||||
private int worstAlignment = -1;
|
||||
private int maxChunksPerEntry = -1;
|
||||
private int alignment = UNDEFINED_ALIGNMENT_CONFIG;
|
||||
private long entries = -1L;
|
||||
private double maxBloatFactor = 1.0;
|
||||
private boolean allowSegmentTiering = true;
|
||||
private double nonTieredSegmentsPercentile = 0.99999;
|
||||
private boolean aligned64BitMemoryOperationsAtomic = OS.is64Bit();
|
||||
|
||||
enum ChecksumEntries {YES, NO, IF_PERSISTED}
|
||||
private ChecksumEntries checksumEntries = ChecksumEntries.IF_PERSISTED;
|
||||
|
||||
private boolean putReturnsNull = false;
|
||||
private boolean removeReturnsNull = false;
|
||||
|
||||
/**
|
||||
* Default timeout is 1 minute. Even loopback tests converge often in the course of seconds,
|
||||
* let alone WAN replication over many nodes might take tens of seconds.
|
||||
*
|
||||
* TODO review
|
||||
*/
|
||||
long cleanupTimeout = 1;
|
||||
TimeUnit cleanupTimeoutUnit = TimeUnit.MINUTES;
|
||||
boolean cleanupRemovedEntries = true;
|
||||
|
||||
DefaultValueProvider<K, V> defaultValueProvider = DefaultSpi.defaultValueProvider();
|
||||
|
||||
byte replicationIdentifier = -1;
|
||||
|
||||
MapMethods<K, V, ?> methods = DefaultSpi.mapMethods();
|
||||
MapEntryOperations<K, V, ?> entryOperations = mapEntryOperations();
|
||||
MapRemoteOperations<K, V, ?> remoteOperations = mapRemoteOperations();
|
||||
|
||||
//////////////////////////////
|
||||
// Instance fields
|
||||
|
||||
private boolean replicated;
|
||||
private boolean persisted;
|
||||
|
||||
ChronicleMapBuilder(Class<K> keyClass, Class<V> valueClass) {
|
||||
keyBuilder = new SerializationBuilder<>(keyClass);
|
||||
valueBuilder = new SerializationBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@code ChronicleMapBuilder} instance which is able to {@linkplain #create()
|
||||
* create} maps with the specified key and value classes.
|
||||
@@ -292,6 +275,125 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
return obj != null ? obj + "" : "not configured";
|
||||
}
|
||||
|
||||
private static void checkSizeIsStaticallyKnown(SerializationBuilder builder, String role) {
|
||||
if (builder.sizeIsStaticallyKnown) {
|
||||
throw new IllegalStateException("Size of " + builder.tClass +
|
||||
" instances is constant and statically known, shouldn't be specified via " +
|
||||
"average" + role + "Size() or average" + role + "() methods");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkAverageSize(double averageSize, String role) {
|
||||
if (averageSize <= 0 || isNaN(averageSize) ||
|
||||
Double.isInfinite(averageSize)) {
|
||||
throw new IllegalArgumentException("Average " + role + " size must be a positive, " +
|
||||
"finite number");
|
||||
}
|
||||
}
|
||||
|
||||
private static double averageSizeStoringLength(
|
||||
SerializationBuilder builder, double averageSize) {
|
||||
SizeMarshaller sizeMarshaller = builder.sizeMarshaller();
|
||||
if (averageSize == round(averageSize))
|
||||
return sizeMarshaller.storingLength(round(averageSize));
|
||||
long lower = roundDown(averageSize);
|
||||
long upper = lower + 1;
|
||||
int lowerStoringLength = sizeMarshaller.storingLength(lower);
|
||||
int upperStoringLength = sizeMarshaller.storingLength(upper);
|
||||
if (lowerStoringLength == upperStoringLength)
|
||||
return lowerStoringLength;
|
||||
return lower * (upper - averageSize) + upper * (averageSize - lower);
|
||||
}
|
||||
|
||||
static int greatestCommonDivisor(int a, int b) {
|
||||
if (b == 0) return a;
|
||||
return greatestCommonDivisor(b, a % b);
|
||||
}
|
||||
|
||||
private static int maxDefaultChunksPerAverageEntry(boolean replicated) {
|
||||
// When replicated, having 8 chunks (=> 8 bits in bitsets) per entry seems more wasteful
|
||||
// because when replicated we have bit sets per each remote node, not only allocation
|
||||
// bit set as when non-replicated
|
||||
return replicated ? 4 : 8;
|
||||
}
|
||||
|
||||
private static int estimateSegmentsForEntries(long size) {
|
||||
if (size > 200 << 20)
|
||||
return 256;
|
||||
if (size >= 1 << 20)
|
||||
return 128;
|
||||
if (size >= 128 << 10)
|
||||
return 64;
|
||||
if (size >= 16 << 10)
|
||||
return 32;
|
||||
if (size >= 4 << 10)
|
||||
return 16;
|
||||
if (size >= 1 << 10)
|
||||
return 8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static long headerChecksum(ByteBuffer headerBuffer, int headerSize) {
|
||||
return LongHashFunction.xx_r39().hashBytes(headerBuffer, SIZE_WORD_OFFSET, headerSize + 4);
|
||||
}
|
||||
|
||||
private static void writeNotComplete(
|
||||
FileChannel fileChannel, ByteBuffer headerBuffer, int headerSize) throws IOException {
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, NOT_COMPLETE | DATA | headerSize);
|
||||
headerBuffer.clear().position(SIZE_WORD_OFFSET).limit(SIZE_WORD_OFFSET + 4);
|
||||
writeFully(fileChannel, SIZE_WORD_OFFSET, headerBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ByteBuffer, with self bootstrapping header in [position, limit) range
|
||||
*/
|
||||
private static <K, V> ByteBuffer writeHeader(
|
||||
FileChannel fileChannel, VanillaChronicleMap<K, V, ?> map) throws IOException {
|
||||
ByteBuffer headerBuffer = ByteBuffer.allocate(
|
||||
SELF_BOOTSTRAPPING_HEADER_OFFSET + MAX_BOOTSTRAPPING_HEADER_SIZE);
|
||||
headerBuffer.order(LITTLE_ENDIAN);
|
||||
|
||||
Bytes<ByteBuffer> headerBytes = Bytes.wrapForWrite(headerBuffer);
|
||||
headerBytes.writePosition(SELF_BOOTSTRAPPING_HEADER_OFFSET);
|
||||
Wire wire = new TextWire(headerBytes);
|
||||
wire.getValueOut().typedMarshallable(map);
|
||||
|
||||
int headerLimit = (int) headerBytes.writePosition();
|
||||
int headerSize = headerLimit - SELF_BOOTSTRAPPING_HEADER_OFFSET;
|
||||
// First set readiness bit to READY, to compute checksum correctly
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, READY | DATA | headerSize);
|
||||
|
||||
long checksum = headerChecksum(headerBuffer, headerSize);
|
||||
headerBuffer.putLong(HEADER_OFFSET, checksum);
|
||||
|
||||
// Set readiness bit to NOT_COMPLETE, because the Chronicle Map instance is not actually
|
||||
// ready yet
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, NOT_COMPLETE | DATA | headerSize);
|
||||
|
||||
// Write the size-prefixed blob to the file
|
||||
headerBuffer.position(0).limit(headerLimit);
|
||||
writeFully(fileChannel, 0, headerBuffer);
|
||||
|
||||
headerBuffer.position(SELF_BOOTSTRAPPING_HEADER_OFFSET);
|
||||
return headerBuffer;
|
||||
}
|
||||
|
||||
private static void commitChronicleMapReady(
|
||||
VanillaChronicleHash map, RandomAccessFile raf, ByteBuffer headerBuffer, int headerSize)
|
||||
throws IOException {
|
||||
FileChannel fileChannel = raf.getChannel();
|
||||
// see HCOLL-396
|
||||
map.msync();
|
||||
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, READY | DATA | headerSize);
|
||||
headerBuffer.clear().position(SIZE_WORD_OFFSET).limit(SIZE_WORD_OFFSET + 4);
|
||||
writeFully(fileChannel, SIZE_WORD_OFFSET, headerBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChronicleMapBuilder<K, V> clone() {
|
||||
try {
|
||||
@@ -475,22 +577,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void checkSizeIsStaticallyKnown(SerializationBuilder builder, String role) {
|
||||
if (builder.sizeIsStaticallyKnown) {
|
||||
throw new IllegalStateException("Size of " + builder.tClass +
|
||||
" instances is constant and statically known, shouldn't be specified via " +
|
||||
"average" + role + "Size() or average" + role + "() methods");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkAverageSize(double averageSize, String role) {
|
||||
if (averageSize <= 0 || isNaN(averageSize) ||
|
||||
Double.isInfinite(averageSize)) {
|
||||
throw new IllegalArgumentException("Average " + role + " size must be a positive, " +
|
||||
"finite number");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the constant number of bytes, taken by serialized form of values, put into maps,
|
||||
* created by this builder. This is done by providing the {@code sampleValue}, all values should
|
||||
@@ -562,16 +648,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
return keyBuilder;
|
||||
}
|
||||
|
||||
static class EntrySizeInfo {
|
||||
final double averageEntrySize;
|
||||
final int worstAlignment;
|
||||
|
||||
public EntrySizeInfo(double averageEntrySize, int worstAlignment) {
|
||||
this.averageEntrySize = averageEntrySize;
|
||||
this.worstAlignment = worstAlignment;
|
||||
}
|
||||
}
|
||||
|
||||
private EntrySizeInfo entrySizeInfo() {
|
||||
double size = 0;
|
||||
double keySize = averageKeySize();
|
||||
@@ -643,20 +719,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
return keyBuilder.constantSizeMarshaller() || sampleKey != null;
|
||||
}
|
||||
|
||||
private static double averageSizeStoringLength(
|
||||
SerializationBuilder builder, double averageSize) {
|
||||
SizeMarshaller sizeMarshaller = builder.sizeMarshaller();
|
||||
if (averageSize == round(averageSize))
|
||||
return sizeMarshaller.storingLength(round(averageSize));
|
||||
long lower = roundDown(averageSize);
|
||||
long upper = lower + 1;
|
||||
int lowerStoringLength = sizeMarshaller.storingLength(lower);
|
||||
int upperStoringLength = sizeMarshaller.storingLength(upper);
|
||||
if (lowerStoringLength == upperStoringLength)
|
||||
return lowerStoringLength;
|
||||
return lower * (upper - averageSize) + upper * (averageSize - lower);
|
||||
}
|
||||
|
||||
private int worstAlignmentAssumingChunkSize(
|
||||
long constantSizeBeforeAlignment, int chunkSize) {
|
||||
int alignment = valueAlignment();
|
||||
@@ -687,11 +749,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
this.worstAlignment = worstAlignment;
|
||||
}
|
||||
|
||||
static int greatestCommonDivisor(int a, int b) {
|
||||
if (b == 0) return a;
|
||||
return greatestCommonDivisor(b, a % b);
|
||||
}
|
||||
|
||||
long chunkSize() {
|
||||
if (actualChunkSize > 0)
|
||||
return actualChunkSize;
|
||||
@@ -720,13 +777,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
return (entrySizeInfo().averageEntrySize + chunkSize - 1) / chunkSize;
|
||||
}
|
||||
|
||||
private static int maxDefaultChunksPerAverageEntry(boolean replicated) {
|
||||
// When replicated, having 8 chunks (=> 8 bits in bitsets) per entry seems more wasteful
|
||||
// because when replicated we have bit sets per each remote node, not only allocation
|
||||
// bit set as when non-replicated
|
||||
return replicated ? 4 : 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChronicleMapBuilder<K, V> maxChunksPerEntry(int maxChunksPerEntry) {
|
||||
if (maxChunksPerEntry < 1)
|
||||
@@ -930,22 +980,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
: segmentsForEntries;
|
||||
}
|
||||
|
||||
private static int estimateSegmentsForEntries(long size) {
|
||||
if (size > 200 << 20)
|
||||
return 256;
|
||||
if (size >= 1 << 20)
|
||||
return 128;
|
||||
if (size >= 128 << 10)
|
||||
return 64;
|
||||
if (size >= 16 << 10)
|
||||
return 32;
|
||||
if (size >= 4 << 10)
|
||||
return 16;
|
||||
if (size >= 1 << 10)
|
||||
return 8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChronicleMapBuilder<K, V> actualSegments(int actualSegments) {
|
||||
checkSegments(actualSegments);
|
||||
@@ -1544,7 +1578,7 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
int sizeWord = headerBuffer.getInt(SIZE_WORD_OFFSET);
|
||||
if (!SizePrefixedBlob.isReady(sizeWord)) {
|
||||
if (recover) {
|
||||
LOG.error("size-prefixed blob readiness bit is set to NOT_READY");
|
||||
LOG.error("size-prefixed blob readiness bit is set to NOT_COMPLETE");
|
||||
// the bit will be overwritten to READY in the end of recovery procedure, so nothing
|
||||
// to fix right here
|
||||
} else {
|
||||
@@ -1562,10 +1596,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
return headerBuffer;
|
||||
}
|
||||
|
||||
private static long headerChecksum(ByteBuffer headerBuffer, int headerSize) {
|
||||
return LongHashFunction.xx_r39().hashBytes(headerBuffer, SIZE_WORD_OFFSET, headerSize + 4);
|
||||
}
|
||||
|
||||
private VanillaChronicleMap<K, V, ?> createWithNewFile(
|
||||
VanillaChronicleMap<K, V, ?> map, File file, RandomAccessFile raf,
|
||||
ByteBuffer headerBuffer, int headerSize) throws IOException {
|
||||
@@ -1609,7 +1639,7 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
map.createMappedStoreAndSegments();
|
||||
} else {
|
||||
if (!overrideBuilderConfig)
|
||||
writeNotReady(fileChannel, headerBuffer, headerSize);
|
||||
writeNotComplete(fileChannel, headerBuffer, headerSize);
|
||||
// if overrideBuilderConfig = true, readiness bit is already set
|
||||
// in writeHeader() call
|
||||
map.recover();
|
||||
@@ -1625,63 +1655,6 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeNotReady(
|
||||
FileChannel fileChannel, ByteBuffer headerBuffer, int headerSize) throws IOException {
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, NOT_READY | DATA | headerSize);
|
||||
headerBuffer.clear().position(SIZE_WORD_OFFSET).limit(SIZE_WORD_OFFSET + 4);
|
||||
writeFully(fileChannel, SIZE_WORD_OFFSET, headerBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ByteBuffer, with self bootstrapping header in [position, limit) range
|
||||
*/
|
||||
private static <K, V> ByteBuffer writeHeader(
|
||||
FileChannel fileChannel, VanillaChronicleMap<K, V, ?> map) throws IOException {
|
||||
ByteBuffer headerBuffer = ByteBuffer.allocate(
|
||||
SELF_BOOTSTRAPPING_HEADER_OFFSET + MAX_BOOTSTRAPPING_HEADER_SIZE);
|
||||
headerBuffer.order(LITTLE_ENDIAN);
|
||||
|
||||
Bytes<ByteBuffer> headerBytes = Bytes.wrapForWrite(headerBuffer);
|
||||
headerBytes.writePosition(SELF_BOOTSTRAPPING_HEADER_OFFSET);
|
||||
Wire wire = new TextWire(headerBytes);
|
||||
wire.getValueOut().typedMarshallable(map);
|
||||
|
||||
int headerLimit = (int) headerBytes.writePosition();
|
||||
int headerSize = headerLimit - SELF_BOOTSTRAPPING_HEADER_OFFSET;
|
||||
// First set readiness bit to READY, to compute checksum correctly
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, READY | DATA | headerSize);
|
||||
|
||||
long checksum = headerChecksum(headerBuffer, headerSize);
|
||||
headerBuffer.putLong(HEADER_OFFSET, checksum);
|
||||
|
||||
// Set readiness bit to NOT_READY, because the Chronicle Map instance is not actually
|
||||
// ready yet
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, NOT_READY | DATA | headerSize);
|
||||
|
||||
// Write the size-prefixed blob to the file
|
||||
headerBuffer.position(0).limit(headerLimit);
|
||||
writeFully(fileChannel, 0, headerBuffer);
|
||||
|
||||
headerBuffer.position(SELF_BOOTSTRAPPING_HEADER_OFFSET);
|
||||
return headerBuffer;
|
||||
}
|
||||
|
||||
private static void commitChronicleMapReady(
|
||||
VanillaChronicleHash map, RandomAccessFile raf, ByteBuffer headerBuffer, int headerSize)
|
||||
throws IOException {
|
||||
FileChannel fileChannel = raf.getChannel();
|
||||
// see HCOLL-396
|
||||
map.msync();
|
||||
|
||||
//noinspection PointlessBitwiseExpression
|
||||
headerBuffer.putInt(SIZE_WORD_OFFSET, READY | DATA | headerSize);
|
||||
headerBuffer.clear().position(SIZE_WORD_OFFSET).limit(SIZE_WORD_OFFSET + 4);
|
||||
writeFully(fileChannel, SIZE_WORD_OFFSET, headerBuffer);
|
||||
}
|
||||
|
||||
ChronicleMap<K, V> createWithoutFile() {
|
||||
replicated = replicationIdentifier != -1;
|
||||
persisted = false;
|
||||
@@ -1778,10 +1751,10 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
/**
|
||||
* Inject your SPI code around basic {@code ChronicleMap}'s operations with entries:
|
||||
* removing entries, replacing entries' value and inserting new entries.
|
||||
*
|
||||
*
|
||||
* <p>This affects behaviour of ordinary map.put(), map.remove(), etc. calls, as well as removes
|
||||
* and replacing values <i>during iterations</i>, <i>remote map calls</i> and
|
||||
* <i>internal replication operations</i>.
|
||||
* <i>internal replication operations</i>.
|
||||
*/
|
||||
public ChronicleMapBuilder<K, V> entryOperations(MapEntryOperations<K, V, ?> entryOperations) {
|
||||
Objects.requireNonNull(entryOperations);
|
||||
@@ -1793,7 +1766,7 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
* Inject your SPI around logic of all {@code ChronicleMap}'s operations with individual keys:
|
||||
* from {@link ChronicleMap#containsKey} to {@link ChronicleMap#acquireUsing} and
|
||||
* {@link ChronicleMap#merge}.
|
||||
*
|
||||
*
|
||||
* <p>This affects behaviour of ordinary map calls, as well as <i>remote calls</i>.
|
||||
*/
|
||||
public ChronicleMapBuilder<K, V> mapMethods(MapMethods<K, V, ?> mapMethods) {
|
||||
@@ -1808,5 +1781,21 @@ public final class ChronicleMapBuilder<K, V> implements
|
||||
this.remoteOperations = remoteOperations;
|
||||
return this;
|
||||
}
|
||||
|
||||
enum ChecksumEntries {YES, NO, IF_PERSISTED}
|
||||
|
||||
interface FileIOAction {
|
||||
void fileIOAction() throws IOException;
|
||||
}
|
||||
|
||||
static class EntrySizeInfo {
|
||||
final double averageEntrySize;
|
||||
final int worstAlignment;
|
||||
|
||||
public EntrySizeInfo(double averageEntrySize, int worstAlignment) {
|
||||
this.averageEntrySize = averageEntrySize;
|
||||
this.worstAlignment = worstAlignment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.affinity.AffinityLock;
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import net.openhft.chronicle.core.values.LongValue;
|
||||
import net.openhft.chronicle.values.Values;
|
||||
|
||||
@@ -75,8 +76,8 @@ public class CHMLatencyTestMain {
|
||||
file.delete();
|
||||
ChronicleMap<LongValue, LongValue> countersMap =
|
||||
ChronicleMapBuilder.of(LongValue.class, LongValue.class)
|
||||
.entries(KEYS)
|
||||
.createPersistedTo(file);
|
||||
.entries(KEYS)
|
||||
.createPersistedTo(file);
|
||||
|
||||
// add keys
|
||||
LongValue key = Values.newHeapInstance(LongValue.class);
|
||||
@@ -142,13 +143,8 @@ public class CHMLatencyTestMain {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (running) {
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
while (running && !Thread.currentThread().isInterrupted()) {
|
||||
Jvm.pause(1);
|
||||
long delay = System.nanoTime() - sample;
|
||||
if (delay > 1000 * 1000) {
|
||||
System.out.println("\n" + (System.currentTimeMillis() - START_TIME) + " : Delay of " + delay / 100000 / 10.0 + " ms.");
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.algo.bytes.Access;
|
||||
import net.openhft.chronicle.algo.locks.AcquisitionStrategies;
|
||||
import net.openhft.chronicle.algo.locks.ReadWriteLockingStrategy;
|
||||
import net.openhft.chronicle.algo.locks.TryAcquireOperations;
|
||||
import net.openhft.chronicle.algo.locks.VanillaReadWriteWithWaitsLockingStrategy;
|
||||
import net.openhft.chronicle.bytes.Byteable;
|
||||
import net.openhft.chronicle.map.utility.ProcessInstanceLimiter;
|
||||
import net.openhft.chronicle.values.Array;
|
||||
import net.openhft.chronicle.values.Group;
|
||||
import net.openhft.chronicle.values.Values;
|
||||
@@ -229,14 +229,7 @@ public class CHMTest5 {
|
||||
}
|
||||
|
||||
public static void pause(long pause) {
|
||||
long start = System.currentTimeMillis();
|
||||
long elapsedTime;
|
||||
while ((elapsedTime = System.currentTimeMillis() - start) < pause) {
|
||||
try {
|
||||
Thread.sleep(pause - elapsedTime);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
ProcessInstanceLimiter.pause(pause);
|
||||
}
|
||||
|
||||
public interface CHMTest5Data {
|
||||
|
||||
@@ -19,7 +19,7 @@ package net.openhft.chronicle.map;
|
||||
import com.google.common.primitives.Chars;
|
||||
import net.openhft.chronicle.bytes.Bytes;
|
||||
import net.openhft.chronicle.bytes.BytesMarshallable;
|
||||
import net.openhft.chronicle.core.Maths;
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import net.openhft.chronicle.core.util.SerializableFunction;
|
||||
import net.openhft.chronicle.core.values.*;
|
||||
import net.openhft.chronicle.hash.serialization.DataAccess;
|
||||
@@ -54,6 +54,103 @@ import static net.openhft.chronicle.core.Maths.divideRoundUp;
|
||||
import static net.openhft.chronicle.map.fromdocs.OpenJDKAndHashMapExamplesTest.parseYYYYMMDD;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
enum ToString implements SerializableFunction<Object, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String apply(Object o) {
|
||||
return String.valueOf(o);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface IData extends BytesMarshallable {
|
||||
String getText();
|
||||
|
||||
void setText(@MaxUtf8Length(64) String text);
|
||||
|
||||
int getNumber();
|
||||
|
||||
void setNumber(int number);
|
||||
|
||||
class Data implements IData, BytesMarshallable {
|
||||
static final long MAGIC = 0x8081828384858687L;
|
||||
static final long MAGIC2 = 0xa0a1a2a3a4a5a6a7L;
|
||||
|
||||
String text;
|
||||
int number;
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumber(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMarshallable(@NotNull Bytes in) throws IllegalStateException {
|
||||
long magic = in.readLong();
|
||||
if (magic != MAGIC)
|
||||
throw new AssertionError("Start " + Long.toHexString(magic));
|
||||
text = in.readUTFΔ();
|
||||
number = in.readInt();
|
||||
long magic2 = in.readLong();
|
||||
if (magic2 != MAGIC2)
|
||||
throw new AssertionError("End " + Long.toHexString(magic2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMarshallable(@NotNull Bytes out) {
|
||||
out.writeLong(MAGIC);
|
||||
out.writeUTFΔ(text);
|
||||
out.writeInt(number);
|
||||
out.writeLong(MAGIC2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IBean {
|
||||
long getLong();
|
||||
|
||||
void setLong(long num);
|
||||
|
||||
double getDouble();
|
||||
|
||||
void setDouble(double d);
|
||||
|
||||
int getInt();
|
||||
|
||||
void setInt(int i);
|
||||
|
||||
@Array(length = 7)
|
||||
void setInnerAt(int index, Inner inner);
|
||||
|
||||
Inner getInnerAt(int index);
|
||||
|
||||
/* nested interface - empowering an Off-Heap hierarchical “TIER of prices”
|
||||
as array[ ] value */
|
||||
interface Inner {
|
||||
|
||||
String getMessage();
|
||||
|
||||
void setMessage(@MaxUtf8Length(20) String px);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test enumerates common use cases for keys and values.
|
||||
*/
|
||||
@@ -61,16 +158,47 @@ import static org.junit.Assert.*;
|
||||
public class CHMUseCasesTest {
|
||||
|
||||
|
||||
enum TypeOfMap {SIMPLE, SIMPLE_PERSISTED}
|
||||
|
||||
static volatile int i = 0;
|
||||
private final TypeOfMap typeOfMap;
|
||||
|
||||
Collection<Closeable> closeables = new ArrayList<Closeable>();
|
||||
ChronicleMap map1;
|
||||
ChronicleMap map2;
|
||||
|
||||
public CHMUseCasesTest(TypeOfMap typeOfMap) {
|
||||
this.typeOfMap = typeOfMap;
|
||||
}
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return asList(new Object[][]{
|
||||
{
|
||||
TypeOfMap.SIMPLE
|
||||
},
|
||||
|
||||
{
|
||||
TypeOfMap.SIMPLE_PERSISTED
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static void appendMode(ByteBuffer valueA) {
|
||||
valueA.position(valueA.limit());
|
||||
valueA.limit(valueA.capacity());
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> mapOf(K k, V v, Object... keysAndValues) {
|
||||
Map<K, V> ret = new LinkedHashMap<>();
|
||||
ret.put(k, v);
|
||||
for (int i = 0; i < keysAndValues.length - 1; i += 2) {
|
||||
Object key = keysAndValues[i];
|
||||
Object value = keysAndValues[i + 1];
|
||||
ret.put((K) key, (V) value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
for (Closeable c : closeables) {
|
||||
@@ -86,24 +214,6 @@ public class CHMUseCasesTest {
|
||||
map1 = null;
|
||||
}
|
||||
|
||||
ChronicleMap map1;
|
||||
ChronicleMap map2;
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return asList(new Object[][]{
|
||||
{
|
||||
TypeOfMap.SIMPLE
|
||||
},
|
||||
|
||||
{
|
||||
TypeOfMap.SIMPLE_PERSISTED
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* * waits until map1 and map2 show the same value
|
||||
*
|
||||
@@ -114,15 +224,10 @@ public class CHMUseCasesTest {
|
||||
for (; t < timeOutMs; t++) {
|
||||
if (map1.equals(map2))
|
||||
break;
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Jvm.pause(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void mapChecks() {
|
||||
if (typeOfMap == TypeOfMap.SIMPLE)
|
||||
checkJsonSerialization();
|
||||
@@ -155,7 +260,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void checkJsonSerialization() {
|
||||
File file = null;
|
||||
try {
|
||||
@@ -201,8 +305,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
static volatile int i = 0;
|
||||
|
||||
private <X, Y> ChronicleMap<X, Y> newInstance(ChronicleMapBuilder<X, Y> builder) throws
|
||||
IOException {
|
||||
if (!builder.constantlySizedKeys() && builder.averageKey == null)
|
||||
@@ -238,38 +340,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
static class PrefixStringFunction implements SerializableFunction<String, String> {
|
||||
private final String prefix;
|
||||
|
||||
public PrefixStringFunction(@NotNull String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String s) {
|
||||
return prefix + s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof PrefixStringFunction &&
|
||||
prefix.equals(((PrefixStringFunction) obj).prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface I1 {
|
||||
@Array(length = 10)
|
||||
String getStrAt(int i);
|
||||
void setStrAt(int i, @MaxUtf8Length(10) String str);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArrayOfString() throws ExecutionException, InterruptedException,
|
||||
IOException {
|
||||
@@ -313,7 +383,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCharArrayValue() throws ExecutionException, InterruptedException, IOException {
|
||||
|
||||
@@ -401,7 +470,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEntrySpanningSeveralChunks()
|
||||
throws ExecutionException, InterruptedException, IOException {
|
||||
@@ -424,10 +492,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testKeyValueSizeBySample() throws ExecutionException, InterruptedException,
|
||||
IOException {
|
||||
@@ -444,7 +508,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLargeCharSequenceValue()
|
||||
throws ExecutionException, InterruptedException, IOException {
|
||||
@@ -483,23 +546,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class StringPrefixUnaryOperator
|
||||
implements BiFunction<String, String, String>, Serializable {
|
||||
|
||||
private String prefix;
|
||||
|
||||
StringPrefixUnaryOperator(final String prefix1) {
|
||||
prefix = prefix1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String k, String v) {
|
||||
return prefix + v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStringStringMapMutableValue() throws ExecutionException, InterruptedException, IOException {
|
||||
|
||||
@@ -548,7 +594,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CharSequence is more efficient when object creation is avoided.
|
||||
* * The key can only be on heap and variable length serialised.
|
||||
@@ -598,7 +643,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAcquireUsingWithCharSequence() throws IOException {
|
||||
|
||||
@@ -716,12 +760,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
interface StringValue {
|
||||
CharSequence getValue();
|
||||
void getUsingValue(StringBuilder using);
|
||||
void setValue(@net.openhft.chronicle.values.NotNull @MaxUtf8Length(64) CharSequence value);
|
||||
}
|
||||
|
||||
/**
|
||||
* StringValue represents any bean which contains a String Value
|
||||
*/
|
||||
@@ -1050,7 +1088,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testByteBufferByteBufferDefaultKeyValueMarshaller() throws ExecutionException,
|
||||
InterruptedException, IOException {
|
||||
@@ -1080,7 +1117,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testByteBufferByteBufferMap()
|
||||
throws ExecutionException, InterruptedException, IOException {
|
||||
@@ -1193,11 +1229,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendMode(ByteBuffer valueA) {
|
||||
valueA.position(valueA.limit());
|
||||
valueA.limit(valueA.capacity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteBufferDirectByteBufferMap()
|
||||
throws ExecutionException, InterruptedException, IOException {
|
||||
@@ -1353,12 +1384,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
interface UnsignedIntValue {
|
||||
long getValue();
|
||||
void setValue(@Range(min = 0, max = (1L << 32) - 1) long value);
|
||||
long addValue(long addition);
|
||||
}
|
||||
|
||||
/**
|
||||
* For unsigned int -> unsigned int entries, the key can be on heap or off heap.
|
||||
*/
|
||||
@@ -1583,12 +1608,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
interface UnsignedShortValue {
|
||||
int getValue();
|
||||
void setValue(@Range(min = 0, max = Character.MAX_VALUE) int value);
|
||||
int addValue(int addition);
|
||||
}
|
||||
|
||||
/**
|
||||
* For int -> unsigned short values, the key can be on heap or off heap.
|
||||
*/
|
||||
@@ -1794,12 +1813,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
interface UnsignedByteValue {
|
||||
int getValue();
|
||||
void setValue(@Range(min = 0, max = 255) int value);
|
||||
int addValue(int addition);
|
||||
}
|
||||
|
||||
/**
|
||||
* For int-> byte entries, the key can be on heap or off heap.
|
||||
*/
|
||||
@@ -2114,7 +2127,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For double values, the key can be on heap or off heap.
|
||||
*/
|
||||
@@ -2422,7 +2434,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMapStringStringValue() throws IOException {
|
||||
|
||||
@@ -2523,17 +2534,6 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> mapOf(K k, V v, Object... keysAndValues) {
|
||||
Map<K, V> ret = new LinkedHashMap<>();
|
||||
ret.put(k, v);
|
||||
for (int i = 0; i < keysAndValues.length - 1; i += 2) {
|
||||
Object key = keysAndValues[i];
|
||||
Object value = keysAndValues[i + 1];
|
||||
ret.put((K) key, (V) value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGeneratedDataValue() throws IOException {
|
||||
|
||||
@@ -2599,6 +2599,87 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
|
||||
enum TypeOfMap {SIMPLE, SIMPLE_PERSISTED}
|
||||
|
||||
|
||||
interface I1 {
|
||||
@Array(length = 10)
|
||||
String getStrAt(int i);
|
||||
|
||||
void setStrAt(int i, @MaxUtf8Length(10) String str);
|
||||
}
|
||||
|
||||
interface StringValue {
|
||||
CharSequence getValue();
|
||||
|
||||
void setValue(@net.openhft.chronicle.values.NotNull @MaxUtf8Length(64) CharSequence value);
|
||||
|
||||
void getUsingValue(StringBuilder using);
|
||||
}
|
||||
|
||||
interface UnsignedIntValue {
|
||||
long getValue();
|
||||
|
||||
void setValue(@Range(min = 0, max = (1L << 32) - 1) long value);
|
||||
|
||||
long addValue(long addition);
|
||||
}
|
||||
|
||||
interface UnsignedShortValue {
|
||||
int getValue();
|
||||
|
||||
void setValue(@Range(min = 0, max = Character.MAX_VALUE) int value);
|
||||
|
||||
int addValue(int addition);
|
||||
}
|
||||
|
||||
interface UnsignedByteValue {
|
||||
int getValue();
|
||||
|
||||
void setValue(@Range(min = 0, max = 255) int value);
|
||||
|
||||
int addValue(int addition);
|
||||
}
|
||||
|
||||
static class PrefixStringFunction implements SerializableFunction<String, String> {
|
||||
private final String prefix;
|
||||
|
||||
public PrefixStringFunction(@NotNull String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String s) {
|
||||
return prefix + s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof PrefixStringFunction &&
|
||||
prefix.equals(((PrefixStringFunction) obj).prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringPrefixUnaryOperator
|
||||
implements BiFunction<String, String, String>, Serializable {
|
||||
|
||||
private String prefix;
|
||||
|
||||
StringPrefixUnaryOperator(final String prefix1) {
|
||||
prefix = prefix1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(String k, String v) {
|
||||
return prefix + v;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DataDataAccess extends BytesMarshallableDataAccess<IData.Data> {
|
||||
public DataDataAccess() {
|
||||
super(IData.Data.class);
|
||||
@@ -2626,100 +2707,3 @@ public class CHMUseCasesTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IData extends BytesMarshallable {
|
||||
void setText(@MaxUtf8Length(64) String text);
|
||||
|
||||
void setNumber(int number);
|
||||
|
||||
String getText();
|
||||
|
||||
int getNumber();
|
||||
|
||||
class Data implements IData, BytesMarshallable {
|
||||
static final long MAGIC = 0x8081828384858687L;
|
||||
static final long MAGIC2 = 0xa0a1a2a3a4a5a6a7L;
|
||||
|
||||
String text;
|
||||
int number;
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNumber(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMarshallable(@NotNull Bytes in) throws IllegalStateException {
|
||||
long magic = in.readLong();
|
||||
if (magic != MAGIC)
|
||||
throw new AssertionError("Start " + Long.toHexString(magic));
|
||||
text = in.readUTFΔ();
|
||||
number = in.readInt();
|
||||
long magic2 = in.readLong();
|
||||
if (magic2 != MAGIC2)
|
||||
throw new AssertionError("End " + Long.toHexString(magic2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMarshallable(@NotNull Bytes out) {
|
||||
out.writeLong(MAGIC);
|
||||
out.writeUTFΔ(text);
|
||||
out.writeInt(number);
|
||||
out.writeLong(MAGIC2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ToString implements SerializableFunction<Object, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String apply(Object o) {
|
||||
return String.valueOf(o);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface IBean {
|
||||
long getLong();
|
||||
|
||||
void setLong(long num);
|
||||
|
||||
double getDouble();
|
||||
|
||||
void setDouble(double d);
|
||||
|
||||
int getInt();
|
||||
|
||||
void setInt(int i);
|
||||
|
||||
@Array(length = 7)
|
||||
void setInnerAt(int index, Inner inner);
|
||||
|
||||
Inner getInnerAt(int index);
|
||||
|
||||
/* nested interface - empowering an Off-Heap hierarchical “TIER of prices”
|
||||
as array[ ] value */
|
||||
interface Inner {
|
||||
|
||||
String getMessage();
|
||||
|
||||
void setMessage(@MaxUtf8Length(20) String px);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
@@ -33,10 +34,6 @@ import java.util.concurrent.locks.LockSupport;
|
||||
*/
|
||||
public class ChronicleMapSanityCheckTest {
|
||||
|
||||
enum DummyValue {
|
||||
DUMMY_VALUE
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSanity1() throws IOException, InterruptedException
|
||||
{
|
||||
@@ -107,7 +104,7 @@ public class ChronicleMapSanityCheckTest {
|
||||
|
||||
}, 0, consumerPeriod, consumerTimeUnit);
|
||||
|
||||
Thread.sleep(totalTestTimeMS);
|
||||
Jvm.pause(totalTestTimeMS);
|
||||
|
||||
consumerExecutor.shutdown();
|
||||
try {
|
||||
@@ -124,4 +121,8 @@ public class ChronicleMapSanityCheckTest {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum DummyValue {
|
||||
DUMMY_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import net.openhft.chronicle.hash.ChronicleHashBuilderPrivateAPI;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -45,7 +46,7 @@ public class Issue62ChronicleClient {
|
||||
cityPostalCodesMapBuilder.createPersistedTo(MAP_FILE_B)) {
|
||||
|
||||
LOGGER.info("Starting");
|
||||
Thread.sleep(3000);
|
||||
Jvm.pause(3000);
|
||||
|
||||
for (Entry<String, Long> entry : map.entrySet()) {
|
||||
LOGGER.info("{} : {}", entry.getKey(), entry.getValue());
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import net.openhft.chronicle.hash.ChronicleHashBuilderPrivateAPI;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -25,16 +26,14 @@ import java.nio.file.Paths;
|
||||
|
||||
public class Issue62ChronicleServer {
|
||||
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(Issue62ChronicleServer.class);
|
||||
|
||||
final static String STR =
|
||||
//"This is just a long string, which causes sink to fail for some reason.";
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
|
||||
private final static File MAP_FILE_A =
|
||||
Paths.get(System.getProperty("java.io.tmpdir"), "map.a").toFile();
|
||||
final static File MAP_FILE_B =
|
||||
Paths.get(System.getProperty("java.io.tmpdir"), "map.b").toFile();
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(Issue62ChronicleServer.class);
|
||||
private final static File MAP_FILE_A =
|
||||
Paths.get(System.getProperty("java.io.tmpdir"), "map.a").toFile();
|
||||
|
||||
static void prepare(File file) {
|
||||
if (file.exists())
|
||||
@@ -63,7 +62,7 @@ public class Issue62ChronicleServer {
|
||||
}
|
||||
|
||||
LOGGER.info("Map created");
|
||||
Thread.sleep(15000);
|
||||
Jvm.pause(15000);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import net.openhft.chronicle.set.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -38,8 +39,54 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class Issue63Test {
|
||||
|
||||
// Right now no corresponding "knownUsers" object
|
||||
private ChronicleMap<CharSequence, List<CharSequence>> knownItems;
|
||||
private ChronicleMap<CharSequence, float[]> xVectors;
|
||||
private ChronicleSet<CharSequence> xRecentIDs;
|
||||
private ChronicleMap<CharSequence, float[]> yVectors;
|
||||
private ChronicleSet<CharSequence> yRecentIDs;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new Issue63Test().testChronicleMap();
|
||||
}
|
||||
|
||||
private static void putIntoMapAndRecentSet(
|
||||
ChronicleMap<CharSequence, float[]> map, ChronicleSet<CharSequence> recentSet,
|
||||
String key, float[] value) {
|
||||
try (ExternalMapQueryContext<CharSequence, float[], ?> c = map.queryContext(key);
|
||||
ExternalSetQueryContext<CharSequence, ?> setC = recentSet.queryContext(key)) {
|
||||
if (c.writeLock().tryLock(1, MINUTES) && setC.writeLock().tryLock(1, MINUTES)) {
|
||||
putNoReturn(c, value);
|
||||
addNoReturn(setC);
|
||||
} else {
|
||||
throw new RuntimeException("Dead lock");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <K> void addNoReturn(SetQueryContext<K, ?> c) {
|
||||
SetAbsentEntry<K> setAbsentEntry = c.absentEntry();
|
||||
if (setAbsentEntry != null) {
|
||||
c.insert(setAbsentEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private static <K, V> void putNoReturn(MapQueryContext<K, V, ?> c, V value) {
|
||||
MapEntry<K, V> entry = c.entry();
|
||||
Data<V> newValue = c.wrapValueAsData(value);
|
||||
if (entry != null) {
|
||||
c.replaceValue(entry, newValue);
|
||||
} else {
|
||||
MapAbsentEntry<K, V> absentEntry = c.absentEntry();
|
||||
assert absentEntry != null;
|
||||
c.insert(absentEntry, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void issue63Test() throws Exception {
|
||||
public void issue63test() throws IOException {
|
||||
Path path = Paths.get(System.getProperty("java.io.tmpdir") + "/test-vectors1.dat");
|
||||
if (Files.exists(path)) Files.delete(path);
|
||||
File mapFile = path.toFile();
|
||||
@@ -137,18 +184,7 @@ public class Issue63Test {
|
||||
assertArrayEquals(exp2, xVectors2.get(key2), 0.0f);
|
||||
}
|
||||
|
||||
// Right now no corresponding "knownUsers" object
|
||||
private ChronicleMap<CharSequence, List<CharSequence>> knownItems;
|
||||
private ChronicleMap<CharSequence, float[]> xVectors;
|
||||
private ChronicleSet<CharSequence> xRecentIDs;
|
||||
private ChronicleMap<CharSequence, float[]> yVectors;
|
||||
private ChronicleSet<CharSequence> yRecentIDs;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new Issue63Test().testChronicleMap();
|
||||
}
|
||||
|
||||
void testChronicleMap() throws Exception {
|
||||
void testChronicleMap() throws IOException {
|
||||
int num = 1_000_000;
|
||||
testChronicleMap(System.getProperty("java.io.tmpdir"), num, num);
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
@@ -169,8 +205,7 @@ public class Issue63Test {
|
||||
|
||||
void testChronicleMap(String persistToDir,
|
||||
int numXIDs,
|
||||
int numYIDs)
|
||||
throws Exception {
|
||||
int numYIDs) throws IOException {
|
||||
|
||||
if (!Files.exists(Paths.get(persistToDir))) Files.createDirectory(Paths.get(persistToDir));
|
||||
|
||||
@@ -257,41 +292,6 @@ public class Issue63Test {
|
||||
putIntoMapAndRecentSet(yVectors, yRecentIDs, id, arr);
|
||||
}
|
||||
|
||||
private static void putIntoMapAndRecentSet(
|
||||
ChronicleMap<CharSequence, float[]> map, ChronicleSet<CharSequence> recentSet,
|
||||
String key, float[] value) {
|
||||
try (ExternalMapQueryContext<CharSequence, float[], ?> c = map.queryContext(key);
|
||||
ExternalSetQueryContext<CharSequence, ?> setC = recentSet.queryContext(key)) {
|
||||
if (c.writeLock().tryLock(1, MINUTES) && setC.writeLock().tryLock(1, MINUTES)) {
|
||||
putNoReturn(c, value);
|
||||
addNoReturn(setC);
|
||||
} else {
|
||||
throw new RuntimeException("Dead lock");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static <K> void addNoReturn(SetQueryContext<K, ?> c) {
|
||||
SetAbsentEntry<K> setAbsentEntry = c.absentEntry();
|
||||
if (setAbsentEntry != null) {
|
||||
c.insert(setAbsentEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private static <K, V> void putNoReturn(MapQueryContext<K, V, ?> c, V value) {
|
||||
MapEntry<K, V> entry = c.entry();
|
||||
Data<V> newValue = c.wrapValueAsData(value);
|
||||
if (entry != null) {
|
||||
c.replaceValue(entry, newValue);
|
||||
} else {
|
||||
MapAbsentEntry<K, V> absentEntry = c.absentEntry();
|
||||
assert absentEntry != null;
|
||||
c.insert(absentEntry, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void addKnownItems(ChronicleMap<CharSequence, List<CharSequence>> knownItems,
|
||||
String id, List<CharSequence> items) {
|
||||
try (ExternalMapQueryContext<CharSequence, List<CharSequence>, ?> c =
|
||||
@@ -307,7 +307,7 @@ public class Issue63Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKnownItems() throws Exception {
|
||||
public void testKnownItems() throws IOException {
|
||||
|
||||
ArrayList<CharSequence> averageKnownItems = new ArrayList<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
|
||||
@@ -157,7 +157,7 @@ public class LargeEntriesTest {
|
||||
sb.append(thisThread).append("\n");
|
||||
try {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
Thread.sleep(50);
|
||||
Jvm.pause(50);
|
||||
StackTraceElement[] stackTrace = thisThread.getStackTrace();
|
||||
for (int i = 0; i < 4 && i < stackTrace.length; i++)
|
||||
sb.append("\tat ").append(stackTrace[i]).append("\n");
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import net.openhft.chronicle.map.utility.ProcessInstanceLimiter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
@@ -60,18 +63,11 @@ public class ProcessInstanceLimiterMain implements Runnable {
|
||||
ProcessInstanceLimiterMain limiter = new ProcessInstanceLimiterMain("test", callback);
|
||||
limiter.setMaxNumberOfProcessesOfType("x", 2);
|
||||
limiter.startingProcessOfType("x");
|
||||
Thread.sleep(60L * 1000L);
|
||||
Jvm.pause(60L * 1000L);
|
||||
}
|
||||
|
||||
public static void pause(long pause) {
|
||||
long start = System.currentTimeMillis();
|
||||
long elapsedTime;
|
||||
while ((elapsedTime = System.currentTimeMillis() - start) < pause) {
|
||||
try {
|
||||
Thread.sleep(pause - elapsedTime);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
ProcessInstanceLimiter.pause(pause);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package net.openhft.chronicle.map;
|
||||
|
||||
import net.openhft.chronicle.bytes.BytesStore;
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import net.openhft.chronicle.core.io.Closeable;
|
||||
import net.openhft.chronicle.core.util.SerializableFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -91,13 +92,10 @@ public class ReplicationCheckingMap<K, V> implements ChronicleMap<K, V> {
|
||||
return r1;
|
||||
|
||||
if (i > 30) {
|
||||
try {
|
||||
Thread.sleep(i);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else
|
||||
Jvm.pause(i);
|
||||
} else {
|
||||
Thread.yield();
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(map1, map2);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class PointListSerializationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexSerialization() throws Exception {
|
||||
public void testComplexSerialization() {
|
||||
try (ChronicleMap<String, A> map = ChronicleMapBuilder
|
||||
.of(String.class, A.class)
|
||||
.valueMarshaller(AMarshaller.INSTANCE)
|
||||
|
||||
@@ -153,7 +153,7 @@ public class JSR166TestCase {
|
||||
long ns = millis * 1000 * 1000;
|
||||
for (; ; ) {
|
||||
if (millis > 0L)
|
||||
Thread.sleep(millis);
|
||||
Jvm.pause(millis);
|
||||
else // too short to sleep
|
||||
Thread.yield();
|
||||
long d = ns - (System.nanoTime() - startTime);
|
||||
|
||||
@@ -97,7 +97,7 @@ public class MapCheck {
|
||||
mainTest(key, absent);
|
||||
if ((rep & 3) == 3 && rep < numTests - 1) {
|
||||
shuffle(key);
|
||||
// Thread.sleep(10);
|
||||
// Jvm.pause(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
package net.openhft.chronicle.map.utility;
|
||||
|
||||
import net.openhft.affinity.AffinitySupport;
|
||||
import net.openhft.chronicle.algo.bytes.Access;
|
||||
import net.openhft.chronicle.algo.locks.*;
|
||||
import net.openhft.chronicle.algo.locks.AcquisitionStrategies;
|
||||
import net.openhft.chronicle.algo.locks.ReadWriteLockingStrategy;
|
||||
import net.openhft.chronicle.algo.locks.TryAcquireOperations;
|
||||
import net.openhft.chronicle.algo.locks.VanillaReadWriteWithWaitsLockingStrategy;
|
||||
import net.openhft.chronicle.bytes.Byteable;
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
import net.openhft.chronicle.map.ChronicleMap;
|
||||
import net.openhft.chronicle.map.ChronicleMapBuilder;
|
||||
import net.openhft.chronicle.values.Array;
|
||||
@@ -43,13 +46,13 @@ import static net.openhft.chronicle.algo.bytes.Access.checkedBytesStoreAccess;
|
||||
* shared map (using ChronicleMap) to maintain shared data across any processes
|
||||
* which use a ProcessInstanceLimiter, and checking on startup and regularly
|
||||
* after whether it is allowed to run.
|
||||
*
|
||||
* <p/>
|
||||
* <p>Typically, you need to specify two things to create an instance of
|
||||
* ProcessInstanceLimiter: a path to a file that will hold the shared map; and a
|
||||
* callback object (an instance implementing ProcessInstanceLimiter.Callback) to
|
||||
* handle the various possible callback messages that the ProcessInstanceLimiter
|
||||
* can generate.
|
||||
*
|
||||
* <p/>
|
||||
* <p>Once you have a ProcessInstanceLimiter instance, you specify a type of
|
||||
* process (any string) which will be limited to up to N processes running at
|
||||
* the same time by calling the setMaxNumberOfProcessesOfType() method. Finally
|
||||
@@ -57,18 +60,18 @@ import static net.openhft.chronicle.algo.bytes.Access.checkedBytesStoreAccess;
|
||||
* startingProcessOfType(X). This last is deliberately not done automatically as
|
||||
* you may wish for one type of process to define limitations on other types of
|
||||
* processes.
|
||||
*
|
||||
* <p/>
|
||||
* <p>The are some convenience methods which allow you to quickly specify a limit
|
||||
* without consideration of the above. For example, if during your application
|
||||
* startup you call ProcessInstanceLimiter.limitTo(2), then you need not call
|
||||
* anything else and you have limited your application to running at most 2 JVM
|
||||
* instances of your application. Under the covers, this call is identical to
|
||||
* the sequence:
|
||||
*
|
||||
* <p/>
|
||||
* <p>ProcessInstanceLimiter limiter = new ProcessInstanceLimiter();
|
||||
* limiter.setMaxNumberOfProcessesOfType(processType,numProcesses);
|
||||
* limiter.startingProcessOfType(processType);
|
||||
*
|
||||
* <p/>
|
||||
* <p>This:
|
||||
* 1. Creates a shared file called ProcessInstanceLimiter_DEFAULT_SHARED_MAP_ in
|
||||
* the temp directory to hold an instance of ChronicleMap
|
||||
@@ -83,7 +86,6 @@ import static net.openhft.chronicle.algo.bytes.Access.checkedBytesStoreAccess;
|
||||
*/
|
||||
public class ProcessInstanceLimiter implements Runnable {
|
||||
private static final long DEFAULT_TIME_UPDATE_INTERVAL_MS = 100L;
|
||||
private long timeUpdateInterval = DEFAULT_TIME_UPDATE_INTERVAL_MS;
|
||||
private static final String DEFAULT_SHARED_MAP_NAME = "ProcessInstanceLimiter_DEFAULT_SHARED_MAP_";
|
||||
private static final String DEFAULT_SHARED_MAP_DIRECTORY = System.getProperty("java.io.tmpdir");
|
||||
private static final String DEFAULT_PROCESS_NAME = "_DEFAULT_";
|
||||
@@ -97,6 +99,7 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
private final Callback callback;
|
||||
private final Map<String, Integer> localUpdates = new ConcurrentHashMap<String, Integer>();
|
||||
private final Map<String, String> processTypeToStartTimeType = new ConcurrentHashMap<String, String>();
|
||||
private long timeUpdateInterval = DEFAULT_TIME_UPDATE_INTERVAL_MS;
|
||||
private long startTime;
|
||||
private long[] lastStartTimes;
|
||||
private Map<String, Data> timedata = new ConcurrentHashMap<String, Data>();
|
||||
@@ -151,12 +154,12 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
ProcessInstanceLimiter.limitTo(2);
|
||||
Thread.sleep(60L * 1000L);
|
||||
Jvm.pause(60L * 1000L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method.
|
||||
*
|
||||
* <p/>
|
||||
* <p>Create a ProcessInstanceLimiter instance which is limited to one OS
|
||||
* process instance of the DEFAULT type. This will enforce that any JVM on
|
||||
* the same box which runs the code
|
||||
@@ -172,7 +175,7 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
|
||||
/**
|
||||
* Convenience method.
|
||||
*
|
||||
* <p/>
|
||||
* <p>Create a ProcessInstanceLimiter instance which is limited to
|
||||
* "numProcesses" OS process instances of the DEFAULT type. This will
|
||||
* enforce that any JVM on the same box which runs the code
|
||||
@@ -191,7 +194,7 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
|
||||
/**
|
||||
* Convenience method.
|
||||
*
|
||||
* <p/>
|
||||
* <p>Create a ProcessInstanceLimiter instance which is limited to
|
||||
* "numProcesses" OS process instances of the "processType" type. This will
|
||||
* enforce that any JVM on the same box which runs the code
|
||||
@@ -223,10 +226,8 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
long start = System.currentTimeMillis();
|
||||
long elapsedTime;
|
||||
while ((elapsedTime = System.currentTimeMillis() - start) < pause) {
|
||||
try {
|
||||
Thread.sleep(pause - elapsedTime);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
Thread.interrupted();// clear interruptions.
|
||||
Jvm.pause(pause - elapsedTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +337,7 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
this.callback.tooManyProcessesOfType(processType);
|
||||
return;
|
||||
}
|
||||
//try {Thread.sleep(60L*1000L);} catch (InterruptedException e) {}
|
||||
//try {Jvm.pause(60L*1000L);} catch (InterruptedException e) {}
|
||||
//we've got the lock, now copy the array
|
||||
try {
|
||||
for (int i = 0; i < times1.length; i++) {
|
||||
@@ -459,9 +460,9 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
|
||||
private void unlock(Data data) {
|
||||
try {
|
||||
VanillaReadWriteWithWaitsLockingStrategy.instance()
|
||||
.writeUnlock(checkedBytesStoreAccess(),
|
||||
((Byteable) data).bytesStore(), ((Byteable) data).offset());
|
||||
VanillaReadWriteWithWaitsLockingStrategy.instance()
|
||||
.writeUnlock(checkedBytesStoreAccess(),
|
||||
((Byteable) data).bytesStore(), ((Byteable) data).offset());
|
||||
} catch (IllegalMonitorStateException e) {
|
||||
//odd, but we'll be unlocked either way
|
||||
System.out.println("Unexpected state: " + e);
|
||||
@@ -531,25 +532,30 @@ public class ProcessInstanceLimiter implements Runnable {
|
||||
/**
|
||||
* The Data object holds an array of timestamps and a maximum number of
|
||||
* processes allowed to be running concurrently
|
||||
*
|
||||
* <p/>
|
||||
* <p>The Timelock field is just for locking the time field
|
||||
*/
|
||||
public interface Data {
|
||||
|
||||
/** Ensure lock goes first in flyweight layout, to apply external locking
|
||||
/**
|
||||
* Ensure lock goes first in flyweight layout, to apply external locking
|
||||
*/
|
||||
@Group(0)
|
||||
long getTimeLock();
|
||||
|
||||
void setTimeLock(long timeLock);
|
||||
|
||||
@Group(1)
|
||||
@Array(length = 50)
|
||||
void setTimeAt(int index, long time);
|
||||
|
||||
long getTimeAt(int index);
|
||||
|
||||
@Group(1)
|
||||
int getMaxNumberOfProcessesAllowed();
|
||||
|
||||
void setMaxNumberOfProcessesAllowed(int num);
|
||||
|
||||
boolean compareAndSwapMaxNumberOfProcessesAllowed(int expected, int value);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package net.openhft.chronicle.set;
|
||||
|
||||
import net.openhft.chronicle.core.Jvm;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
@@ -52,21 +54,21 @@ public class Builder {
|
||||
return file;
|
||||
}
|
||||
|
||||
public static void waitTillEqual(Map map1, Map map2, int timeOutMs) throws InterruptedException {
|
||||
public static void waitTillEqual(Map map1, Map map2, int timeOutMs) {
|
||||
int numberOfTimesTheSame = 0;
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int t = 0; t < timeOutMs + 100; t++) {
|
||||
// not map1.equals(map2), the reason is described above
|
||||
if (map1.equals(map2)) {
|
||||
numberOfTimesTheSame++;
|
||||
Thread.sleep(1);
|
||||
Jvm.pause(1);
|
||||
if (numberOfTimesTheSame == 10) {
|
||||
System.out.println("same");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
Thread.sleep(1);
|
||||
Jvm.pause(1);
|
||||
if (System.currentTimeMillis() - startTime > timeOutMs)
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user