/* * Copyright (C) 2012, 2016 higherfrequencytrading.com * 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 <http://www.gnu.org/licenses/>. */ package net.openhft.chronicle.map; import net.openhft.chronicle.core.values.IntValue; import net.openhft.chronicle.values.Values; import org.junit.Test; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import static java.nio.ByteOrder.LITTLE_ENDIAN; import static net.openhft.chronicle.algo.hashing.LongHashFunction.xx_r39; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; public class TrickyContextCasesTest { @Test(expected = IllegalStateException.class) public void nestedContextsSameKeyTest() { ChronicleMap<Integer, IntValue> map = ChronicleMapBuilder .of(Integer.class, IntValue.class) .entries(1).create(); IntValue v = Values.newHeapInstance(IntValue.class); v.setValue(2); map.put(1, v); try (ExternalMapQueryContext<Integer, IntValue, ?> q = map.queryContext(1)) { q.writeLock().lock(); // assume the value is 2 IntValue v2 = q.entry().value().get(); // this call should throw ISE, as accessing the key 1 in a nested context, but if not... map.remove(1); v.setValue(3); map.put(2, v); // prints 3 System.out.println(v2.getValue()); } } @Test(expected = Exception.class) public void testPutShouldBeWriteLocked() throws ExecutionException, InterruptedException { ChronicleMap<Integer, byte[]> map = ChronicleMapBuilder .of(Integer.class, byte[].class) .averageValue(new byte[1]) .entries(100).actualSegments(1).create(); map.put(1, new byte[] {1}); map.put(2, new byte[] {2}); try (ExternalMapQueryContext<Integer, byte[], ?> q = map.queryContext(1)) { MapEntry<Integer, byte[]> entry = q.entry(); // acquires read lock implicitly assertNotNull(entry); Executors.newFixedThreadPool(1).submit(() -> { // this call should try to acquire write lock, that should lead to dead lock // but if not... // relocates the entry for the key 1 after the entry for 2, under update lock map.put(1, new byte[] {1, 2, 3, 4, 5}); // puts the entry for 3 at the place of the entry for the key 1, under update lock map.put(3, new byte[] {3}); }).get(); // prints [3] System.out.println(Arrays.toString(entry.value().get())); } } @Test public void testHashCollision() { try (ChronicleMap<ByteBuffer, Integer> map = ChronicleMap .of(ByteBuffer.class, Integer.class) .constantKeySizeBySample(ByteBuffer.allocate(128)) .entries(2) .create()) { ByteBuffer key1 = ByteBuffer.allocate(128).order(LITTLE_ENDIAN); key1.putLong(0, 1); key1.putLong(32, 2); ByteBuffer key2 = ByteBuffer.allocate(128).order(LITTLE_ENDIAN); key2.putLong(0, 1 + 0xBA79078168D4BAFL); key2.putLong(32, 2 + 0x9C90005B80000000L); ByteBuffer key3 = ByteBuffer.allocate(128).order(LITTLE_ENDIAN); key3.putLong(0, 1 + 0xBA79078168D4BAFL * 2); key3.putLong(32, 2 + 0x9C90005B80000000L * 2); assertEquals(xx_r39().hashBytes(key1), xx_r39().hashBytes(key2)); assertEquals(xx_r39().hashBytes(key1), xx_r39().hashBytes(key3)); try (ExternalMapQueryContext<ByteBuffer, Integer, ?> c1 = map.queryContext(key1)) { c1.writeLock().lock(); c1.insert(c1.absentEntry(), c1.wrapValueAsData(1)); try (ExternalMapQueryContext<ByteBuffer, Integer, ?> c2 = map.queryContext(key2)) { c2.writeLock().lock(); c2.insert(c2.absentEntry(), c2.wrapValueAsData(2)); c1.remove(c1.entry()); map.put(key3, 3); c2.replaceValue(c2.entry(), c2.wrapValueAsData(22)); } } assertEquals(2, map.size()); assertEquals((Integer) 22, map.get(key2)); assertEquals((Integer) 3, map.get(key3)); } } }