diff --git a/README.md b/README.md index be5077093..96b77e9c4 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ Click here to get the [Latest Version Number](http://search.maven.org/#search%7C * [Identifier for Replication](https://github.com/OpenHFT/Chronicle-Map#identifier-for-replication) * [Bootstrapping](https://github.com/OpenHFT/Chronicle-Map#bootstrapping) * [Clustering](https://github.com/OpenHFT/Chronicle-Map#cluster) +* [Known Issues](https://github.com/OpenHFT/Chronicle-Map#known-issues) +* [Development Tasks - JIRA] (https://higherfrequencytrading.atlassian.net/browse/HCOLL) + #### Examples @@ -56,10 +59,11 @@ A Chronicle Map implements the java.util.concurrent.ConcurrentMap, so on the fac ## When to use #### When to use HashMap -Most of the time. - If you compare HashMap, ConcurrentHashMap and Chronicle Map, most of the maps in your system are likely to be HashMap. This is because HashMap is lightweight and synchronized HashMap works well for lightly contended use cases. By contention I mean, how many threads on average are trying to use a Map. One reason you can't have many contended resources, is that you only have so many CPUs and they can only be accessing so many resources at once (ideally no more than one or two per thread at a time) + + + #### When to use ConcurrentHashMap Some of the time. @@ -434,6 +438,19 @@ cluster = clusterReplicatorBuilder.create(); map = clusterReplicatorBuilder.create((short) 1, builder); ``` +#### Known Issues + +Chronicle map lets you assign a map larger than your available memory, This will however impact performance as Chronicle Map will have to page the segments to and from disk as data is written or read from the map. This feature works brilliantly on Linux but unfortunately its not supported on Windows, if you use more memory than is physically available you will experience the following error : + +```java +Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) +j sun.misc.Unsafe.compareAndSwapLong(Ljava/lang/Object;JJJ)Z+0 +j net.openhft.lang.io.NativeBytes.compareAndSwapLong(JJJ)Z+13 +j net.openhft.lang.io.AbstractBytes.tryLockNanos8a(JJ)Z+12 +j net.openhft.lang.io.AbstractBytes.tryLockNanosLong(JJ)Z+41 +j net.openhft.collections.AbstractVanillaSharedHashMap$Segment.lock()V+12 +``` + # Example : Replicating data between process on different servers Lets assume that we had two server, lets call them server1 and server2, if we wished to share a map between them, this is how we could set it up diff --git a/src/main/java/net/openhft/chronicle/map/AbstractChannelReplicator.java b/src/main/java/net/openhft/chronicle/map/AbstractChannelReplicator.java index deade5e8d..dd7b30e2b 100644 --- a/src/main/java/net/openhft/chronicle/map/AbstractChannelReplicator.java +++ b/src/main/java/net/openhft/chronicle/map/AbstractChannelReplicator.java @@ -329,16 +329,39 @@ abstract class AbstractChannelReplicator implements Closeable { final Thread thread = new Thread(new Runnable() { public void run() { + SelectableChannel socketChannel = null; try { if (reconnectionInterval > 0) Thread.sleep(reconnectionInterval); - SelectableChannel socketChannel = doConnect(); - closeables.add(socketChannel); + socketChannel = doConnect(); + + try { + closeables.add(socketChannel); + } catch (IllegalStateException e) { + + // close could have be called from another thread, while we were in Thread.sleep() + // which would cause a IllegalStateException + closeQuietly(socketChannel); + return; + } + AbstractConnector.this.socketChannel = socketChannel; + } catch (Exception e) { - LOG.error("", e); + closeQuietly(socketChannel); + LOG.debug("", e); + } + } + + private void closeQuietly(SelectableChannel socketChannel) { + if (socketChannel == null) + return; + try { + socketChannel.close(); + } catch (Exception e1) { + // do nothing } } }); @@ -346,6 +369,8 @@ abstract class AbstractChannelReplicator implements Closeable { thread.setName(name); thread.setDaemon(true); thread.start(); + + } public void setSuccessfullyConnected() { diff --git a/src/main/java/net/openhft/chronicle/map/CloseablesManager.java b/src/main/java/net/openhft/chronicle/map/CloseablesManager.java index e4a655530..a115c234e 100644 --- a/src/main/java/net/openhft/chronicle/map/CloseablesManager.java +++ b/src/main/java/net/openhft/chronicle/map/CloseablesManager.java @@ -29,7 +29,7 @@ final class CloseablesManager implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(CloseablesManager.class.getName()); private boolean isClosed = false; - private List closeables = new ArrayList(); + private final List closeables = new ArrayList(); private void checkState() { if (isClosed) @@ -55,6 +55,8 @@ final class CloseablesManager implements Closeable { public synchronized void closeQuietly(Closeable closeable) { try { close(closeable); + } catch (IllegalStateException e) { + // this can occur if already closed ( for example closed is called from another thread ) } catch (IOException e) { LOG.error("", e); } @@ -84,4 +86,8 @@ final class CloseablesManager implements Closeable { // do nothing } } + + boolean isClosed() { + return isClosed; + } } diff --git a/src/test/java/net/openhft/chronicle/map/Issue42.java b/src/test/java/net/openhft/chronicle/map/Issue42.java index aae50126b..01814ed3a 100644 --- a/src/test/java/net/openhft/chronicle/map/Issue42.java +++ b/src/test/java/net/openhft/chronicle/map/Issue42.java @@ -25,8 +25,20 @@ import static net.openhft.chronicle.map.Alignment.NO_ALIGNMENT; import static net.openhft.chronicle.map.Builder.getPersistenceFile; public class Issue42 { + + private static String OS = System.getProperty("os.name").toLowerCase(); + + public static boolean isWindows() { + return (OS.indexOf("win") >= 0); + } + + @Test public void crashJVMWindowsTest() throws IOException { + + if (!isWindows()) + return; + final ChronicleMap map = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) .entryAndValueAlignment(NO_ALIGNMENT)