diff --git a/src/main/java/net/openhft/chronicle/ChronicleHash.java b/src/main/java/net/openhft/chronicle/ChronicleHash.java new file mode 100644 index 000000000..996213937 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/ChronicleHash.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed 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 net.openhft.chronicle; + +import net.openhft.chronicle.map.ChronicleMap; +import net.openhft.chronicle.set.ChronicleSet; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; + +/** + * This interface defines common {@link ChronicleMap} and {@link ChronicleSet}, related to off-heap + * memory management and file-mapping. Not usable by itself. + */ +public interface ChronicleHash extends Closeable { + /** + * Returns the file this hash container mapped to, i. e. when it is created by + * {@link ChronicleHashBuilder#create()} call, or {@code null} if it is purely in-memory, + * i. e. if it is created by {@link ChronicleHashBuilder#create()} call. + * + * @return the file this {@link ChronicleMap} or {@link ChronicleSet} is mapped to, + * or {@code null} if it is not mapped to any file + * @see ChronicleHashBuilder#create(File) + * @see ChronicleHashBuilder#create() + */ + File file(); + + /** + * Releases the off-heap memory, used by this hash container and resources, used by replication, + * if any. However, if hash container (hence off-heap memory, used by it) is mapped to the file + * and there are other instances mapping the same data on the server across JVMs, the memory + * won't be actually freed on operation system level. I. e. this method call doesn't affect + * other {@link ChronicleMap} or {@link ChronicleSet} instances mapping the same data. + * + *

If you won't call this method, memory would be held at least until next garbage + * collection. This could be a problem if, for example, you target rare garbage collections, + * but load and drop {@code ChronicleHash}es regularly. + * + *

+ * TODO what about commit guarantees, when ChronicleMap is used with memory-mapped files, if + * {@code ChronicleMap}/{@code ChronicleSet} closed/not? + * + *

After this method call behaviour of all methods of {@code ChronicleMap} + * or {@code ChronicleSet} is undefined. Any method call on the map might throw + * any exception, or even JVM crash. Shortly speaking, don't call use map closing. + */ + @Override + void close() throws IOException; +} diff --git a/src/main/java/net/openhft/chronicle/ChronicleHashBuilder.java b/src/main/java/net/openhft/chronicle/ChronicleHashBuilder.java index c37ea2cda..505d9614b 100644 --- a/src/main/java/net/openhft/chronicle/ChronicleHashBuilder.java +++ b/src/main/java/net/openhft/chronicle/ChronicleHashBuilder.java @@ -1,6 +1,7 @@ package net.openhft.chronicle; import net.openhft.chronicle.map.*; +import net.openhft.chronicle.set.ChronicleSet; import net.openhft.chronicle.set.ChronicleSetBuilder; import org.jetbrains.annotations.NotNull; @@ -26,8 +27,8 @@ import java.util.concurrent.TimeUnit; * @param the concrete builder type, i. e. {@link ChronicleMapBuilder} * or {@link ChronicleSetBuilder} */ -public interface ChronicleHashBuilder> - extends Cloneable { +public interface ChronicleHashBuilder> extends Cloneable { B clone(); diff --git a/src/main/java/net/openhft/chronicle/TimeProvider.java b/src/main/java/net/openhft/chronicle/TimeProvider.java index 5f48dcb51..8f5885286 100644 --- a/src/main/java/net/openhft/chronicle/TimeProvider.java +++ b/src/main/java/net/openhft/chronicle/TimeProvider.java @@ -17,7 +17,7 @@ package net.openhft.chronicle; import net.openhft.chronicle.map.ChronicleMap; -import net.openhft.chronicle.map.ChronicleSet; +import net.openhft.chronicle.set.ChronicleSet; import java.io.ObjectStreamException; import java.io.Serializable; diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java index e6858b4b8..57385aa98 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java @@ -16,24 +16,103 @@ package net.openhft.chronicle.map; +import net.openhft.chronicle.ChronicleHash; +import net.openhft.chronicle.map.serialization.BytesReader; +import net.openhft.lang.io.Bytes; +import net.openhft.lang.io.serialization.BytesMarshaller; +import net.openhft.lang.io.serialization.ObjectFactory; +import net.openhft.lang.model.Byteable; + import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.util.Map; import java.util.concurrent.ConcurrentMap; -public interface ChronicleMap extends ConcurrentMap, Closeable { +/** + * Extension of {@link ConcurrentMap} interface, stores the data off-heap. + * + *

For information on + *

+ * see {@link ChronicleMapBuilder} documentation. + * + *

Functionally this interface defines some methods supporting garbage-free off-heap programming: + * {@link #getUsing(Object, Object)}, {@link #acquireUsing(Object, Object)}. + * + *

Note that {@code ChronicleMap} extends {@link Closeable}, don't forget + * to {@linkplain #close() close} map when it is no longer needed. + * + * @param the map key type + * @param the map value type + */ +public interface ChronicleMap extends ConcurrentMap, ChronicleHash { /** - * @return size as a long. - * @see java.util.Map size() + * Returns the number of entries in this map. + * + * @return number of entries in this map + * @see Map#size() */ long longSize(); /** - * Get a value for a key if available. If the value is Byteable, it will be assigned to - * reference the value, instead of copying the data for zero copy access to the collection. + * Returns the value to which the specified key is mapped, or {@code null} if this map contains + * no mapping for the key. * - * @param key to lookup. - * @param value to reuse if possible. If null, a new object will be created. - * @return value found or null if not. + *

If the specified key if absent in the map, {@linkplain + * ChronicleMapBuilder#defaultValue(Object) default value} is taken or {@linkplain + * ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) default value provider} + * is called (depending on what was configured last in {@code ChronicleMapBuilder}). + * If it results to something different from {@code null}, this object is put to this map for + * the specified key, and then returned from this {@code get()} call. Otherwise, if default + * value is {@code null} and default value provider returns {@code null}, the map remains + * unchanged and {@code null} is returned. + * + *

If the value class allows reusing, particularly if it is a {@link Byteable} subclass, + * consider {@link #getUsing(Object, Object)} method instead of this to reduce garbage creation. + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped after this method call, + * or {@code null} if no value is mapped + * @see #getUsing(Object, Object) + */ + @Override + V get(Object key); + + /** + * Returns the value to which the specified key is mapped, read to the provided {@code value} + * object, if possible, or returns {@code null}, if this map contains no mapping for the key. + * + *

If the specified key is present in the map, the value data is read to the provided + * {@code value} object via value marshaller's {@link BytesMarshaller#read(Bytes, Object) + * read(Bytes, value)} or value reader's {@link BytesReader#read(Bytes, long, Object) + * read(Bytes, size, value)} method, depending on what deserialization strategy is configured + * on this map builder. If the value deserializer is able to reuse the given {@code value} + * object, calling this method instead of {@link #get(Object)} could help to reduce garbage + * creation. + * + *

The provided {@code value} object is allowed to be {@code null}, in this case + * {@code map.getUsing(key, null)} call is semantically equivalent to simple + * {@code map.get(key)} call. + * + *

If the specified key if absent in the map, {@linkplain + * ChronicleMapBuilder#defaultValue(Object) default value} is taken or {@linkplain + * ChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) default value provider} + * is called. If it results to something different from {@code null}, this object is put + * to this map for the specified key, and then a new value is returned from this + * {@code getUsing()} call. Otherwise, if default value is {@code null} and default value + * provider, if specified, returns {@code null}, the map remains unchanged and {@code null} + * is returned. The provided {@code value} is untouched anyway in this case. + * + * @param key the key whose associated value is to be returned + * @param value the object to read value data in, if possible + * @return the value mapped to the specified key after this method call, or {@code null} if no + * value is mapped + * @see #get(Object) + * @see ChronicleMapBuilder#valueMarshallerAndFactory(BytesMarshaller, ObjectFactory) */ V getUsing(K key, V value); @@ -41,15 +120,10 @@ public interface ChronicleMap extends ConcurrentMap, Closeable { * Acquire a value for a key, creating if absent. If the value is Byteable, it will be assigned * to reference the value, instead of copying the data. * - * @param key to lookup. + * @param key the key whose associated value is to be returned * @param value to reuse if possible. If null, a new object will be created. * @return value created or found. */ V acquireUsing(K key, V value); - /** - * @return The file or directory for this ChronicleMap - */ - File file(); - } diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleSet.java b/src/main/java/net/openhft/chronicle/map/ChronicleSet.java deleted file mode 100644 index 5d3b92abf..000000000 --- a/src/main/java/net/openhft/chronicle/map/ChronicleSet.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.openhft.chronicle.map; - -import java.io.Closeable; -import java.util.Set; - -public interface ChronicleSet extends Set, Closeable { - public long longSize(); -} diff --git a/src/main/java/net/openhft/chronicle/set/ChronicleSet.java b/src/main/java/net/openhft/chronicle/set/ChronicleSet.java new file mode 100644 index 000000000..c167caad1 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/set/ChronicleSet.java @@ -0,0 +1,10 @@ +package net.openhft.chronicle.set; + +import net.openhft.chronicle.ChronicleHash; + +import java.io.Closeable; +import java.util.Set; + +public interface ChronicleSet extends Set, ChronicleHash { + public long longSize(); +} diff --git a/src/main/java/net/openhft/chronicle/set/SetFromMap.java b/src/main/java/net/openhft/chronicle/set/SetFromMap.java index 47fd84883..cb513154c 100644 --- a/src/main/java/net/openhft/chronicle/set/SetFromMap.java +++ b/src/main/java/net/openhft/chronicle/set/SetFromMap.java @@ -17,8 +17,8 @@ package net.openhft.chronicle.set; import net.openhft.chronicle.map.ChronicleMap; -import net.openhft.chronicle.map.ChronicleSet; +import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.AbstractSet; @@ -115,6 +115,11 @@ class SetFromMap extends AbstractSet return m.longSize(); } + @Override + public File file() { + return m.file(); + } + @Override public void close() throws IOException { m.close(); diff --git a/src/test/java/net/openhft/chronicle/set/SetForTCPSocketReplicationTest3way.java b/src/test/java/net/openhft/chronicle/set/SetForTCPSocketReplicationTest3way.java index 57576a192..057f70161 100644 --- a/src/test/java/net/openhft/chronicle/set/SetForTCPSocketReplicationTest3way.java +++ b/src/test/java/net/openhft/chronicle/set/SetForTCPSocketReplicationTest3way.java @@ -16,7 +16,6 @@ package net.openhft.chronicle.set; -import net.openhft.chronicle.map.ChronicleSet; import org.junit.After; import org.junit.Before; import org.junit.Test;