/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.ignite.internal.util.offheap.unsafe; import java.util.HashSet; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.D; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; /** * */ public class GridOffheapSnapTreeSelfTest extends GridCommonAbstractTest { /** * Test for memory leaks. * * @throws Exception If failed. */ @SuppressWarnings("TypeMayBeWeakened") public void testMemoryMultithreaded() throws Exception { final TestPointerFactory f = new TestPointerFactory(1000); final GridUnsafeMemory mem = new GridUnsafeMemory(25000); final GridUnsafeGuard guard = new GridUnsafeGuard(); final GridOffHeapSnapTreeMap<TestPointer, TestPointer> m = new GridOffHeapSnapTreeMap<>( f, f, mem, guard); final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); for (int j = 1; j < 20; j++) { final int max = Math.min(1000, j * 20); multithreaded(new Runnable() { @SuppressWarnings({"LockAcquiredButNotSafelyReleased", "StatementWithEmptyBody", "UnusedDeclaration", "unchecked"}) @Override public void run() { Random rnd = new Random(); for (int i = 0; i < 100000; i++) { int x = 1 + rnd.nextInt(max); TestPointer fx = f.createPointer(x); boolean put = rnd.nextInt(2) != 0; lock.readLock().lock(); guard.begin(); try { if (put) m.put(fx, fx); else m.remove(fx); } finally { lock.readLock().unlock(); guard.end(); } if (i % 100 == 0) { lock.writeLock().lock(); GridOffHeapSnapTreeMap<TestPointer, TestPointer> m2; try { m.validate(); m2 = m.clone(); assert m2.equals(m); } finally { lock.writeLock().unlock(); } m2.validate(); for (GridOffHeapSmartPointer p : m2.values()) assertTrue(((TestPointer)p).refs.get() >= 2); m2.close(); } } } }, 29); m.validate(); assertEquals(m.size(), m.nodes(true)); assertFalse(mem.allocatedSize() == 0); X.println(String.valueOf(mem.allocatedSize())); int refs = 0; for (TestPointer ptr : f.ptrs) refs += ptr.refs.get(); assertEquals(m.size() * 2 + (m.nodes(false) - m.size()), refs); if (j % 2 == 0) continue; guard.begin(); try { for (int i = 1; i <= max; i++) m.remove(f.createPointer(i)); } finally { guard.end(); } assertEquals(0, m.size()); assertTrue(m.isEmpty()); assertEquals(0, m.nodes(false)); for (TestPointer ptr : f.ptrs) { refs = ptr.refs.get(); assertEquals(0, refs); } } m.close(); assertEquals(0, mem.allocatedSize()); } /** * @throws Exception If failed. */ public void testKeyLockMultithreaded() throws Exception { final GridOffHeapSnapTreeMap.KeyLock lock = new GridOffHeapSnapTreeMap.KeyLock(); final int[] ints = new int[2000]; final int iterations = 1000000; final int threads = 37; final AtomicInteger sum0 = new AtomicInteger(); final ConcurrentMap<Integer, Object> locked = new ConcurrentHashMap<>(); multithreaded(new Runnable() { @Override public void run() { Random rnd = new Random(); for (int i = 0; i < iterations; i++) sum0.addAndGet(increment(rnd, 1 + rnd.nextInt(ints.length - 1), new HashSet<Integer>())); } /** * @param rnd Random. * @param idx Index. * @param locIdxs Locked indexes. * @return Count of incremented cells. */ int increment(Random rnd, int idx, HashSet<Integer> locIdxs) { if (idx >= ints.length) return 0; int res = 1; GridOffHeapSnapTreeMap.KeyLock.Lock l = lock.lock(idx); try { assertTrue(locIdxs.add(idx)); Object check = F.asList(l == null ? Boolean.TRUE : l, Thread.currentThread().getName(), idx); Object check2 = locked.putIfAbsent(idx, check); if (check2 != null) fail(">> " + check + " <><><> " + check2); ints[idx]++; assertNull(lock.lock(idx)); if (rnd.nextInt(10) > 2) // Test reentrancy. res += increment(rnd, idx + 1 + rnd.nextInt(3), locIdxs); assertTrue(locIdxs.remove(idx)); assertSame(check, locked.remove(idx)); } finally { if (l != null) l.unlock(); } return res; } }, threads); int sum = 0; for (int i = 1; i < ints.length; i++) { GridOffHeapSnapTreeMap.KeyLock.Lock l = lock.lock(i); assertNotNull(l); sum += ints[i]; assertNull(lock.lock(i)); l.unlock(); } assertEquals(sum0.get(), sum); X.println("Sum: ", sum); } /** * Test pointer factory. */ private static final class TestPointerFactory implements GridOffHeapSmartPointerFactory<TestPointer> { /** */ private TestPointer[] ptrs; /** * @param cnt Pointers count. */ TestPointerFactory(int cnt) { ptrs = new TestPointer[cnt]; for (int i = 0 ; i < ptrs.length; i++) ptrs[i] = new TestPointer(i + 1); } /** {@inheritDoc} */ @Override public TestPointer createPointer(long ptr) { assert ptr > 0 && ptr <= ptrs.length : ptr + " " + Long.toBinaryString(ptr); return ptrs[((int)ptr) - 1]; } } /** * Test pointer. */ private static final class TestPointer implements GridOffHeapSmartPointer, Comparable<TestPointer> { /** */ private final AtomicInteger refs = new AtomicInteger(); /** */ private final int ptr; /** * @param ptr Pointer. */ private TestPointer(int ptr) { this.ptr = ptr; } @Override public long pointer() { return ptr; } @Override public void incrementRefCount() { refs.incrementAndGet(); } @Override public void decrementRefCount() { int res = refs.decrementAndGet(); assert res >= 0 : D.dumpWithStop() + ptr; } @SuppressWarnings("SubtractionInCompareTo") @Override public int compareTo(TestPointer o) { assert o.refs.get() > 0 : o.refs.get(); return (int)(o.pointer() - pointer()); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TestPointer that = (TestPointer)o; return ptr == that.ptr; } @Override public int hashCode() { throw new IllegalStateException(); } @Override public String toString() { return ptr + "(" + refs + ")"; } } }