From 0f6d37ae244b0cfc00cf41e8a02773aa957c43d2 Mon Sep 17 00:00:00 2001 From: leventov Date: Sat, 19 Nov 2016 20:27:21 -0600 Subject: [PATCH] Add ExitHookTest --- .../openhft/chronicle/map/ExitHookTest.java | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/test/java/net/openhft/chronicle/map/ExitHookTest.java 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; + } +}