diff --git a/src/test/java/net/openhft/chronicle/map/ExitHookTest.java b/src/test/java/net/openhft/chronicle/map/ExitHookTest.java
new file mode 100644
index 000000000..aec592ae2
--- /dev/null
+++ b/src/test/java/net/openhft/chronicle/map/ExitHookTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 Roman Leventov
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package net.openhft.chronicle.map;
+
+import net.openhft.chronicle.core.OS;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+public class ExitHookTest {
+
+ private static final int KEY = 1;
+
+ @Rule
+ public TemporaryFolder folder = new TemporaryFolder();
+
+ @Test
+ public void testExitHook() throws IOException, InterruptedException {
+ if (!OS.isLinux() && !OS.isMacOSX())
+ return; // This test runs only in Unix-like OSes
+ File mapFile = folder.newFile("map");
+ System.out.println("map file: " + mapFile.getAbsolutePath());
+ // Create a process which opens the map, acquires the lock and "hangs" for 30 seconds
+ Process process = startOtherProcess(mapFile);
+ // Let the other process actually reach the moment when it locks the map
+ // (JVM startup and chronicle map creation are not instant)
+ Thread.sleep(10_000);
+ // Interrupt that process to trigger Chronicle Map's shutdown hooks
+ interruptProcess(getPidOfProcess(process));
+ process.waitFor();
+ System.out.println("other process exit code: " + process.exitValue());
+ ChronicleMap map = createMap(mapFile);
+ try (ExternalMapQueryContext c = map.queryContext(KEY)) {
+ // Test that we are able to lock the segment, i. e. the lock was released in other
+ // process thanks to shutdown hooks
+ c.writeLock().lock();
+ }
+ }
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+ System.out.println("Other process started");
+ File mapFile = new File(args[0]);
+ ChronicleMap map = createMap(mapFile);
+ try (ExternalMapQueryContext c = map.queryContext(KEY)) {
+ c.writeLock().lock();
+ Thread.sleep(30_000);
+ }
+ }
+
+ private static ChronicleMap createMap(File mapFile) throws IOException {
+ return ChronicleMap
+ .of(Integer.class, Integer.class)
+ .entries(1)
+ .createPersistedTo(mapFile);
+ }
+
+ // http://stackoverflow.com/a/7835467/648955
+ private void interruptProcess(long pidOfProcess) throws IOException {
+ Runtime.getRuntime().exec("kill -SIGINT " + pidOfProcess);
+ }
+
+ // http://stackoverflow.com/a/723914/648955
+ private Process startOtherProcess(File mapFile) throws IOException {
+ String javaHome = System.getProperty("java.home");
+ String javaBin = javaHome +
+ File.separator + "bin" +
+ File.separator + "java";
+ String classpath = System.getProperty("java.class.path");
+ System.out.println("Classpath: " + classpath);
+ String className = ExitHookTest.class.getCanonicalName();
+
+ ProcessBuilder builder = new ProcessBuilder(
+ javaBin, "-cp", classpath, className, mapFile.getAbsolutePath());
+ builder.inheritIO();
+ return builder.start();
+ }
+
+ // http://stackoverflow.com/a/33171840/648955
+ public static long getPidOfProcess(Process p) {
+ long pid = -1;
+
+ try {
+ if (p.getClass().getName().equals("java.lang.UNIXProcess")) {
+ Field f = p.getClass().getDeclaredField("pid");
+ f.setAccessible(true);
+ pid = f.getLong(p);
+ f.setAccessible(false);
+ }
+ } catch (Exception e) {
+ pid = -1;
+ }
+ return pid;
+ }
+}