GEODE-656: add ReferenceCountHelper junit tests This closes #135
Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/32a69873 Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/32a69873 Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/32a69873 Branch: refs/heads/feature/GEODE-1255 Commit: 32a69873682a91a444b8e41ef92e6768625cfd95 Parents: fdf9e9a Author: Scott Jewell <sjew...@pivotal.io> Authored: Thu Mar 3 11:26:12 2016 -0800 Committer: Darrel Schneider <dschnei...@pivotal.io> Committed: Wed May 4 16:50:12 2016 -0700 ---------------------------------------------------------------------- .../internal/offheap/ReferenceCountHelper.java | 218 ++--- .../offheap/ReferenceCountHelperImpl.java | 304 ++++++ .../offheap/ReferenceCountHelperImplTest.java | 974 +++++++++++++++++++ .../offheap/ReferenceCountHelperJUnitTest.java | 208 ++++ 4 files changed, 1546 insertions(+), 158 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/32a69873/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelper.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelper.java b/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelper.java index f6696b0..563ba4d 100644 --- a/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelper.java +++ b/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelper.java @@ -17,15 +17,9 @@ package com.gemstone.gemfire.internal.offheap; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import com.gemstone.gemfire.internal.cache.RegionEntry; - /** * This class provides static methods to help * debug off-heap reference count problems. @@ -33,48 +27,31 @@ import com.gemstone.gemfire.internal.cache.RegionEntry; * To enable free operation tracking set: -Dgemfire.trackOffHeapFreedRefCounts=true. */ public class ReferenceCountHelper { - private ReferenceCountHelper() { - // no instances allowed - } - final static private boolean trackRefCounts = Boolean.getBoolean("gemfire.trackOffHeapRefCounts"); - final static private boolean trackFreedRefCounts = Boolean.getBoolean("gemfire.trackOffHeapFreedRefCounts"); - final static private ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces; - final static private ConcurrentMap<Long, List<RefCountChangeInfo>> freedStacktraces; - final static private ThreadLocal<Object> refCountOwner; - final static private ThreadLocal<AtomicInteger> refCountReenterCount; - final static private Object SKIP_REF_COUNT_TRACKING = new Object(); - final static private List<RefCountChangeInfo> LOCKED = Collections.emptyList(); - - static { - if (trackRefCounts) { - stacktraces = new ConcurrentHashMap<Long, List<RefCountChangeInfo>>(); - if (trackFreedRefCounts) { - freedStacktraces = new ConcurrentHashMap<Long, List<RefCountChangeInfo>>(); - } else { - freedStacktraces = null; - } - refCountOwner = new ThreadLocal<Object>(); - refCountReenterCount = new ThreadLocal<AtomicInteger>(); - } else { - stacktraces = null; - freedStacktraces = null; - refCountOwner = null; - refCountReenterCount = null; - } + + public static final String TRACK_OFFHEAP_REFERENCES = "gemfire.trackOffHeapRefCounts"; + public static final String TRACK_OFFHEAP_FREES = "gemfire.trackOffHeapFreedRefCounts"; + + private static final ReferenceCountHelperImpl inst = new ReferenceCountHelperImpl(Boolean.getBoolean(TRACK_OFFHEAP_REFERENCES), Boolean.getBoolean(TRACK_OFFHEAP_FREES)); + + /* Do not allow any instances */ + private ReferenceCountHelper() {} + + static ReferenceCountHelperImpl getInstance() { + return inst; } /** * Returns true if reference count tracking is enabled. */ public static boolean trackReferenceCounts() { - return trackRefCounts; + return getInstance().trackReferenceCounts(); } /** * Returns true if free operation tracking is enabled. */ public static boolean trackFreedReferenceCounts() { - return trackFreedRefCounts; + return getInstance().trackFreedReferenceCounts(); } /** @@ -85,31 +62,7 @@ public class ReferenceCountHelper { * Calling this method is a noop if !trackReferenceCounts. */ public static void setReferenceCountOwner(Object owner) { - if (trackReferenceCounts()) { - if (refCountOwner.get() != null) { - AtomicInteger ai = refCountReenterCount.get(); - if (owner != null) { - ai.incrementAndGet(); - } else { - if (ai.decrementAndGet() <= 0) { - refCountOwner.set(null); - ai.set(0); - } - } - } else { - AtomicInteger ai = refCountReenterCount.get(); - if (ai == null) { - ai = new AtomicInteger(0); - refCountReenterCount.set(ai); - } - if (owner != null) { - ai.set(1); - } else { - ai.set(0); - } - refCountOwner.set(owner); - } - } + getInstance().setReferenceCountOwner(owner); } /** @@ -117,12 +70,7 @@ public class ReferenceCountHelper { * Calling this method is a noop and returns null if !trackReferenceCounts. */ public static Object createReferenceCountOwner() { - Object result = null; - if (trackReferenceCounts()) { - result = new Object(); - setReferenceCountOwner(result); - } - return result; + return getInstance().createReferenceCountOwner(); } /** @@ -134,14 +82,21 @@ public class ReferenceCountHelper { * after the allocation or free is done. */ public static void skipRefCountTracking() { - setReferenceCountOwner(SKIP_REF_COUNT_TRACKING); + getInstance().skipRefCountTracking(); + } + + /** + * Returns true if currently tracking reference counts. + */ + public static boolean isRefCountTracking() { + return getInstance().isRefCountTracking(); } /** * Call this method to undo a call to skipRefCountTracking. */ public static void unskipRefCountTracking() { - setReferenceCountOwner(null); + getInstance().unskipRefCountTracking(); } /** @@ -149,89 +104,14 @@ public class ReferenceCountHelper { * the given Chunk address. */ public static List<RefCountChangeInfo> getRefCountInfo(long address) { - if (!trackReferenceCounts()) return null; - List<RefCountChangeInfo> result = stacktraces.get(address); - while (result != null && !stacktraces.replace(address, result, LOCKED)) { - result = stacktraces.get(address); - } - return result; - } - - /** - * Returns a list of any free operation tracking information. - * This is used to describe who did the previous free(s) when an extra one - * ends up being done and fails. - */ - public static List<RefCountChangeInfo> getFreeRefCountInfo(long address) { - if (!trackReferenceCounts() || !trackFreedReferenceCounts()) return null; - return freedStacktraces.get(address); + return getInstance().getRefCountInfo(address); } /** * Used internally to report that a reference count has changed. - */ - + */ static void refCountChanged(Long address, boolean decRefCount, int rc) { - final Object owner = refCountOwner.get(); - if (owner == SKIP_REF_COUNT_TRACKING) { - return; - } - List<RefCountChangeInfo> list = stacktraces.get(address); - if (list == null) { - List<RefCountChangeInfo> newList = new ArrayList<RefCountChangeInfo>(); - List<RefCountChangeInfo> old = stacktraces.putIfAbsent(address, newList); - if (old == null) { - list = newList; - } else { - list = old; - } - } - if (decRefCount) { - if (owner != null) { - synchronized (list) { - for (int i=0; i < list.size(); i++) { - RefCountChangeInfo info = list.get(i); - if (owner instanceof RegionEntry) { - // use identity comparison on region entries since sqlf does some wierd stuff in the equals method - if (owner == info.getOwner()) { - if (info.getUseCount() > 0) { - info.decUseCount(); - } else { - list.remove(i); - } - return; - } - } else if (owner.equals(info.getOwner())) { - if (info.getUseCount() > 0) { - info.decUseCount(); - } else { - list.remove(i); - } - return; - } - } - } - } - } - if (list == LOCKED) { - MemoryAllocatorImpl.debugLog("refCount " + (decRefCount ? "deced" : "inced") + " after orphan detected for @" + Long.toHexString(address), true); - return; - } - RefCountChangeInfo info = new RefCountChangeInfo(decRefCount, rc, owner); - synchronized (list) { - // if (list.size() == 16) { - // debugLog("dumping @" + Long.toHexString(address) + " history=" + list, false); - // list.clear(); - // } - for (RefCountChangeInfo e: list) { - if (e.isSameCaller(info)) { - // No need to add it just increment useCount - e.incUseCount(); - return; - } - } - list.add(info); - } + getInstance().refCountChanged(address, decRefCount, rc); } /** @@ -239,16 +119,38 @@ public class ReferenceCountHelper { * that a free has happened of the given address. */ static void freeRefCountInfo(Long address) { - if (!trackReferenceCounts()) return; - List<RefCountChangeInfo> freedInfo = stacktraces.remove(address); - if (freedInfo == LOCKED) { - MemoryAllocatorImpl.debugLog("freed after orphan detected for @" + Long.toHexString(address), true); - } else if (trackFreedReferenceCounts()) { - if (freedInfo != null) { - freedStacktraces.put(address, freedInfo); - } else { - freedStacktraces.remove(address); - } - } + getInstance().freeRefCountInfo(address); + } + + /** + * Returns the thread local owner + */ + static Object getReferenceCountOwner() { + return getInstance().getReferenceCountOwner(); + } + + /** + * Returns the thread local count of the + * number of times ref count has been updated + */ + static AtomicInteger getReenterCount() { + return getInstance().getReenterCount(); + } + + /** + * Returns a list of any free operation tracking information. + * This is used to describe who did the previous free(s) when an extra one + * ends up being done and fails. + */ + public static List<RefCountChangeInfo> getFreeRefCountInfo(long address) { + return getInstance().getFreeRefCountInfo(address); + } + + /** + * Returns a list of any reference count tracking information for + * the given Chunk address without locking. + */ + static List<RefCountChangeInfo> peekRefCountInfo(long address) { + return getInstance().peekRefCountInfo(address); } } http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/32a69873/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImpl.java b/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImpl.java new file mode 100644 index 0000000..571c2d1 --- /dev/null +++ b/geode-core/src/main/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImpl.java @@ -0,0 +1,304 @@ +/* + * 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 com.gemstone.gemfire.internal.offheap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +import com.gemstone.gemfire.internal.cache.RegionEntry; + +/** + * All access to this class should be done through + * the static methods of ReferenceCountHelper. + */ +class ReferenceCountHelperImpl { + private boolean trackRefCounts; + private boolean trackFreedRefCounts; + private ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces; + private ConcurrentMap<Long, List<RefCountChangeInfo>> freedStacktraces; + private ThreadLocal<Object> refCountOwner; + private ThreadLocal<AtomicInteger> refCountReenterCount; + final static private Object SKIP_REF_COUNT_TRACKING = new Object(); + final static private List<RefCountChangeInfo> LOCKED = Collections.emptyList(); + + ReferenceCountHelperImpl(boolean trackRefCounts, boolean trackFreedRefCounts) { + this.trackRefCounts = trackRefCounts; + this.trackFreedRefCounts = trackFreedRefCounts; + if (trackRefCounts) { + stacktraces = new ConcurrentHashMap<Long, List<RefCountChangeInfo>>(); + if (trackFreedRefCounts) { + freedStacktraces = new ConcurrentHashMap<Long, List<RefCountChangeInfo>>(); + } else { + freedStacktraces = null; + } + refCountOwner = new ThreadLocal<Object>(); + refCountReenterCount = new ThreadLocal<AtomicInteger>(); + } else { + stacktraces = null; + freedStacktraces = null; + refCountOwner = null; + refCountReenterCount = null; + } + } + + /** + * Returns true if reference count tracking is enabled. + */ + public boolean trackReferenceCounts() { + return trackRefCounts; + } + + /** + * Returns true if free operation tracking is enabled. + */ + public boolean trackFreedReferenceCounts() { + return trackFreedRefCounts; + } + + /** + * Optional call to tell the tracker the logical "owner" + * of the reference count. For example you could set + * the particular EntryEventImpl instance that incremented + * the reference count and is responsible for decrementing it. + * Calling this method is a noop if !trackReferenceCounts. + */ + public void setReferenceCountOwner(Object owner) { + if (trackReferenceCounts()) { + if (refCountOwner.get() != null) { + AtomicInteger ai = refCountReenterCount.get(); + if (owner != null) { + ai.incrementAndGet(); + } else { + if (ai.decrementAndGet() <= 0) { + refCountOwner.set(null); + ai.set(0); + } + } + } else { + AtomicInteger ai = refCountReenterCount.get(); + if (ai == null) { + ai = new AtomicInteger(0); + refCountReenterCount.set(ai); + } + if (owner != null) { + ai.set(1); + } else { + ai.set(0); + } + refCountOwner.set(owner); + } + } + } + + /** + * Create, set, and return a generic reference count owner object. + * Calling this method is a noop and returns null if !trackReferenceCounts. + */ + public Object createReferenceCountOwner() { + Object result = null; + if (trackReferenceCounts()) { + result = new Object(); + setReferenceCountOwner(result); + } + return result; + } + + /** + * Call this method before incrementing a reference count + * if you know that tracking is not needed because you know + * that the allocate and free will always be done in the same + * code block. + * Callers of this method must also call unskipRefCountTracking + * after the allocation or free is done. + */ + public void skipRefCountTracking() { + setReferenceCountOwner(SKIP_REF_COUNT_TRACKING); + } + + /** + * Returns true if currently tracking reference counts. + */ + public boolean isRefCountTracking() { + if (!trackReferenceCounts()) return false; + return !(getReferenceCountOwner()==SKIP_REF_COUNT_TRACKING); + } + + /** + * Call this method to undo a call to skipRefCountTracking. + */ + public void unskipRefCountTracking() { + setReferenceCountOwner(null); + } + + /** + * Returns a list of any reference count tracking information for + * the given Chunk address. + */ + public List<RefCountChangeInfo> getRefCountInfo(long address) { + if (!trackReferenceCounts()) return null; + List<RefCountChangeInfo> result = stacktraces.get(address); + + getReferenceCountInfoTestHook(stacktraces, address); + + while (result != null && !stacktraces.replace(address, result, LOCKED)) { + result = stacktraces.get(address); + } + return result; + } + + /* + * This method is overridden during testing to allow simulation of a + * concurrent update occurring between stacktraces.get and stacktraces.replace + */ + protected void getReferenceCountInfoTestHook(ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces, long address) {} + + /** + * Returns a list of any reference count tracking information for + * the given Chunk address without locking. + */ + public List<RefCountChangeInfo> peekRefCountInfo(long address) { + if (!trackReferenceCounts()) return null; + return stacktraces.get(address); + } + + /** + * Used internally to report that a reference count has changed. + */ + void refCountChanged(Long address, boolean decRefCount, int rc) { + if (!trackReferenceCounts()) return; + final Object owner = refCountOwner.get(); + if (owner == SKIP_REF_COUNT_TRACKING) { + return; + } + List<RefCountChangeInfo> list = stacktraces.get(address); + if (list == null) { + List<RefCountChangeInfo> newList = new ArrayList<RefCountChangeInfo>(); + + refCountChangedTestHook(address, decRefCount, rc); + + List<RefCountChangeInfo> old = stacktraces.putIfAbsent(address, newList); + if (old == null) { + list = newList; + } else { + list = old; + } + } + if (decRefCount) { + if (owner != null) { + synchronized (list) { + for (int i=0; i < list.size(); i++) { + RefCountChangeInfo info = list.get(i); + if (owner instanceof RegionEntry) { + // use identity comparison on region entries since sqlf does some wierd stuff in the equals method + if (owner == info.getOwner()) { + if (info.getUseCount() > 0) { + info.decUseCount(); + } else { + list.remove(i); + } + return; + } + } else if (owner.equals(info.getOwner())) { + if (info.getUseCount() > 0) { + info.decUseCount(); + } else { + list.remove(i); + } + return; + } + } + } + } + } + if (list == LOCKED) { + MemoryAllocatorImpl.debugLog("refCount " + (decRefCount ? "deced" : "inced") + " after orphan detected for @" + Long.toHexString(address), true); + return; + } + RefCountChangeInfo info = new RefCountChangeInfo(decRefCount, rc, owner); + synchronized (list) { + // if (list.size() == 16) { + // debugLog("dumping @" + Long.toHexString(address) + " history=" + list, false); + // list.clear(); + // } + for (RefCountChangeInfo e: list) { + if (e.isSameCaller(info)) { + // No need to add it just increment useCount + e.incUseCount(); + return; + } + } + list.add(info); + } + } + + /* + * This method is overridden during testing to allow simulation + * of a race to be the first to reference a given address + */ + protected void refCountChangedTestHook(Long address, boolean decRefCount, int rc) { } + + /** + * Called internally when free operations are tracked to record + * that a free has happened of the given address. + */ + void freeRefCountInfo(Long address) { + if (!trackReferenceCounts()) return; + List<RefCountChangeInfo> freedInfo = stacktraces.remove(address); + if (freedInfo == LOCKED) { + MemoryAllocatorImpl.debugLog("freed after orphan detected for @" + Long.toHexString(address), true); + } else if (trackFreedReferenceCounts()) { + if (freedInfo != null) { + freedStacktraces.put(address, freedInfo); + } else { + freedStacktraces.remove(address); + } + } + } + + /** + * Returns the thread local owner + */ + Object getReferenceCountOwner() { + if (!trackReferenceCounts()) return null; + return refCountOwner.get(); + } + + /** + * Returns the thread local count of the + * number of times ref count has been updated + */ + AtomicInteger getReenterCount() { + if (!trackReferenceCounts()) return null; + return refCountReenterCount.get(); + } + + /** + * Returns a list of any free operation tracking information. + * This is used to describe who did the previous free(s) when an extra one + * ends up being done and fails. + */ + public List<RefCountChangeInfo> getFreeRefCountInfo(long address) { + if (!trackReferenceCounts() || !trackFreedReferenceCounts()) return null; + return freedStacktraces.get(address); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/32a69873/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImplTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImplTest.java b/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImplTest.java new file mode 100644 index 0000000..e43362f --- /dev/null +++ b/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperImplTest.java @@ -0,0 +1,974 @@ +/* + * 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 com.gemstone.gemfire.internal.offheap; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import static org.mockito.Mockito.*; + +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.*; + +import com.gemstone.gemfire.internal.cache.RegionEntry; +import com.gemstone.gemfire.test.junit.categories.UnitTest; + +/* + * PowerMock used in this test to verify static method MemoryAllocatorImpl.debugLog + */ +@Category(UnitTest.class) +@RunWith(PowerMockRunner.class) +@PowerMockIgnore({ "*.UnitTest" }) +@PrepareForTest({ MemoryAllocatorImpl.class }) +public class ReferenceCountHelperImplTest { + + ReferenceCountHelperImpl rchi; + + @Rule + public SystemOutRule sor = new SystemOutRule(); + + @Test + public void doTrackReferenceCountsWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + assertTrue(rchi.trackReferenceCounts()); + } + + @Test + public void doTrackReferenceCountsWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + assertTrue(rchi.trackReferenceCounts()); + } + + @Test + public void doTrackReferenceCountsWithTrackRefsFalseAndTrackFreesTrue() { + rchi = getFalseTrue(); + assertFalse(rchi.trackReferenceCounts()); + } + + @Test + public void doTrackReferenceCountsWithTrackRefsFalseAndTrackFreesFalse() { + rchi = getFalseFalse(); + assertFalse(rchi.trackReferenceCounts()); + } + + @Test + public void doTrackFreedReferenceCountsWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + assertTrue(rchi.trackFreedReferenceCounts()); + } + + @Test + public void doTrackFreedReferenceCountsWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + assertFalse(rchi.trackFreedReferenceCounts()); + } + + @Test + public void doTrackFreedReferenceCountsWithTrackRefsFalseAndTrackFreesTrue() { + rchi = getFalseTrue(); + assertTrue(rchi.trackFreedReferenceCounts()); + } + + @Test + public void doTrackFreedReferenceCountsWithTrackRefsFalseAndTrackFreesFalse() { + rchi = getFalseFalse(); + assertFalse(rchi.trackFreedReferenceCounts()); + } + + @Test + public void doSkipRefCountTrackingWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + Object preOwner = rchi.getReferenceCountOwner(); + + rchi.skipRefCountTracking(); + Object postOwner = rchi.getReferenceCountOwner(); + + assertTrue(postOwner!=preOwner); // skip sets owner to SKIP_REF_COUNT_TRACKING + + assertFalse(rchi.isRefCountTracking()); + + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + rchi.unskipRefCountTracking(); + postOwner = rchi.getReferenceCountOwner(); + assertEquals(postOwner, preOwner); + + assertTrue(rchi.isRefCountTracking()); + } + + @Test + public void doSkipRefCountTrackingWithTrackRefsFalseAndTrackFreesTrue() { + rchi = getFalseTrue(); + Object preOwner = rchi.getReferenceCountOwner(); + assertEquals(null, preOwner); // getReferenceCountOwner returns null if not tracking + + rchi.skipRefCountTracking(); + assertFalse(rchi.isRefCountTracking()); + + rchi.unskipRefCountTracking(); + assertFalse(rchi.isRefCountTracking()); // system prop not set + } + + @Test + public void doSkipRefCountTrackingWithTrackRefsFalseAndTrackFreesFalse() { + rchi = getFalseFalse(); + Object preOwner = rchi.getReferenceCountOwner(); + assertEquals(null, preOwner); // getReferenceCountOwner returns null if not tracking + + rchi.skipRefCountTracking(); + assertFalse(rchi.isRefCountTracking()); + + rchi.unskipRefCountTracking(); + assertFalse(rchi.isRefCountTracking()); // system prop not set + } + + @Test + public void doSkipRefCountTrackingWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + Object preOwner = rchi.getReferenceCountOwner(); + + rchi.skipRefCountTracking(); + Object postOwner = rchi.getReferenceCountOwner(); + + assertTrue(postOwner!=preOwner); // skip sets owner to SKIP_REF_COUNT_TRACKING + + assertFalse(rchi.isRefCountTracking()); + + rchi.unskipRefCountTracking(); + postOwner = rchi.getReferenceCountOwner(); + assertEquals(postOwner, preOwner); + + assertTrue(rchi.isRefCountTracking()); + } + + @Test + public void doSetReferenceCountOwnerWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + String owner = null; + rchi.setReferenceCountOwner(owner); + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + + owner = new String("SomeOwner"); + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), owner); + + String owner2 = new String("SomeOwner2"); + rchi.setReferenceCountOwner(owner2); + ai = rchi.getReenterCount(); + assertEquals(2, ai.get()); + assertTrue(rchi.getReferenceCountOwner()!=owner2); // stays original owner until cnt = 0 + + String owner3 = null; + rchi.setReferenceCountOwner(owner3); + ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), owner); + + owner = null; + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), null); + + RegionEntry re = mock(RegionEntry.class); + rchi.setReferenceCountOwner(re); + ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), re); + + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(0, list.size()); + + } + + @Test + public void doSetReferenceCountOwnerWithTrackRefsFalseAndTrackFreesTrue() { + rchi = getFalseTrue(); + String owner = null; + rchi.setReferenceCountOwner(owner); + assertEquals(rchi.getReferenceCountOwner(), owner); + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(null, ai); + } + + @Test + public void doSetReferenceCountOwnerWithTrackRefsFalseAndTrackFreesFalse() { + rchi = getFalseFalse(); + String owner = null; + rchi.setReferenceCountOwner(owner); + assertEquals(rchi.getReferenceCountOwner(), owner); + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(null, ai); + } + + @Test + public void doSetReferenceCountOwnerWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + String owner = null; + rchi.setReferenceCountOwner(owner); + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + + owner = new String("SomeOwner"); + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), owner); + + String owner2 = new String("SomeOwner2"); + rchi.setReferenceCountOwner(owner2); + ai = rchi.getReenterCount(); + assertEquals(2, ai.get()); + assertTrue(rchi.getReferenceCountOwner()!=owner2); // stays original owner until cnt = 0 + + String owner3 = null; + rchi.setReferenceCountOwner(owner3); + ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), owner); + + owner = null; + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), null); + } + + @Test + public void doCreateReferenceCountOwnerWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + Object owner = rchi.createReferenceCountOwner(); + assertFalse(owner==null); + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + + owner = null; + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), null); + } + + @Test + public void doCreateReferenceCountOwnerWithTrackRefsFalseAndTrackFreesTrue() { + rchi = getFalseTrue(); + Object owner = rchi.createReferenceCountOwner(); + assertTrue(owner==null); + } + + @Test + public void doCreateReferenceCountOwnerWithTrackRefsFalseAndTrackFreesFalse() { + rchi = getFalseFalse(); + Object owner = rchi.createReferenceCountOwner(); + assertTrue(owner==null); + } + + @Test + public void doCreateReferenceCountOwnerWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + Object owner = rchi.createReferenceCountOwner(); + assertFalse(owner==null); + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + + owner = null; + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), null); + } + + @Test + public void doRefCountChangedNoOwnerWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + rchi.freeRefCountInfo(address); // quick check of free of nonexistent info + + Object owner = rchi.getReferenceCountOwner(); + assertTrue(owner==null); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); // inc and dec are tracked in different changeinfo objects (?) + rcci = list.get(1); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); + rcci = list.get(1); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); // list contains 2 entries from inc/dec done above + + List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // no freeRefCountInfo calls yet + + rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list + + List<RefCountChangeInfo> freeInfo2 = rchi.getFreeRefCountInfo(address); + assertEquals(2, freeInfo2.size()); // the inc/dec info moved to freeRefCountInfo list + + list = rchi.getRefCountInfo(address); + assertEquals(null, list); // the inc/dec ref count list should now be null + } + + @Test + public void doRefCountChangedNoOwnerWithTrackRefsFalseAndTrackFreesTrue() { + + rchi = getFalseTrue(); + + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + Object owner = rchi.getReferenceCountOwner(); + assertTrue(owner==null); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // no freeRefCountInfo calls yet + + rchi.freeRefCountInfo(address); // noop when not tracking + + freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // should still be null + } + + @Test + public void doRefCountChangedNoOwnerWithTrackRefsFalseAndTrackFreesFalse() { + + rchi = getFalseFalse(); + + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + Object owner = rchi.getReferenceCountOwner(); + assertTrue(owner==null); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // no freeRefCountInfo calls yet + + rchi.freeRefCountInfo(address); // noop when not tracking + + freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // should still be null + } + + @Test + public void doRefCountChangedNoOwnerWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + Object owner = rchi.getReferenceCountOwner(); + assertTrue(owner==null); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); // inc and dec are tracked in different changeinfo objects (?) + rcci = list.get(1); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); + rcci = list.get(1); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); // list contains 2 entries from inc/dec done above + + List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // no freeRefCountInfo calls yet + + rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list + + List<RefCountChangeInfo> freeInfo2 = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo2); // not tracking freed info + + list = rchi.getRefCountInfo(address); + assertEquals(null, list); // the inc/dec ref count list should now be null + } + + @Test + public void doRefCountChangedWithOwnerWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + Object owner = rchi.createReferenceCountOwner(); + assertFalse(owner==null); + + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + + owner = null; + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), null); + + owner = rchi.createReferenceCountOwner(); + assertFalse(owner==null); + + ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); // inc and dec are tracked in different changeinfo objects (?) + rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.getRefCountInfo(address); + assertEquals(0, list.size()); + } + + @Test + public void doRefCountChangedWithOwnerWithTrackRefsFalseAndTrackFreesTrue() { + + rchi = getFalseTrue(); + + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + Object owner = rchi.createReferenceCountOwner(); + assertTrue(owner==null); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); // inc and dec are tracked in different changeinfo objects (?) + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // no freeRefCountInfo calls yet + + rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list + + freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // the inc/dec info moved to freeRefCountInfo list + } + + @Test + public void doRefCountChangedWithOwnerWithTrackRefsFalseAndTrackFreesFalse() { + + rchi = getFalseFalse(); + + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + Object owner = rchi.createReferenceCountOwner(); + assertTrue(owner==null); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); // inc and dec are tracked in different changeinfo objects (?) + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // no freeRefCountInfo calls yet + + rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list + + freeInfo = rchi.getFreeRefCountInfo(address); + assertEquals(null, freeInfo); // the inc/dec info moved to freeRefCountInfo list + } + + @Test + public void doRefCountChangedWithOwnerWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + Long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + Object owner = rchi.createReferenceCountOwner(); + assertFalse(owner==null); + + AtomicInteger ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + + owner = null; + rchi.setReferenceCountOwner(owner); + ai = rchi.getReenterCount(); + assertEquals(0, ai.get()); + assertEquals(rchi.getReferenceCountOwner(), null); + + owner = rchi.createReferenceCountOwner(); + assertFalse(owner==null); + + ai = rchi.getReenterCount(); + assertEquals(1, ai.get()); + + rchi.refCountChanged(address, decRefCount, rc); + List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); // inc and dec are tracked in different changeinfo objects (?) + rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(0, list.size()); + } + + @Test + public void doGetRefCountInfoWithTrackRefsTrueAndTrackFreesTrue() { + rchi = getTrueTrue(); + long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + List<RefCountChangeInfo> list = null; + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo + assertEquals(1, info.size()); + rcci = info.get(0); + assertEquals(1, rcci.getUseCount()); + + list = rchi.peekRefCountInfo(address); + assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty) + } + + @Test + public void doRefCountChangedAfterGetRefCountInfoWithTrackRefsTrueAndTrackFreesTrue() throws Exception { + rchi = getTrueTrue(); + long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + List<RefCountChangeInfo> list = null; + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo + assertEquals(1, info.size()); + rcci = info.get(0); + assertEquals(1, rcci.getUseCount()); + + list = rchi.peekRefCountInfo(address); + assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty) + + sor.mute(); // Mute system out + + PowerMockito.spy(MemoryAllocatorImpl.class); // Watch the impl for invocation of debugLog + + rchi.refCountChanged(address, decRefCount, rc); // this line should fail. no inc after getInfo allowed + PowerMockito.verifyStatic(); + MemoryAllocatorImpl.debugLog("refCount inced after orphan detected for @1000", true); + + decRefCount = true; + + rchi.refCountChanged(address, decRefCount, rc); // this line should fail. no inc after getInfo allowed + PowerMockito.verifyStatic(); + MemoryAllocatorImpl.debugLog("refCount deced after orphan detected for @1000", true); + + rchi.freeRefCountInfo(address); // this line should fail. no free after getInfo allowed + PowerMockito.verifyStatic(); + MemoryAllocatorImpl.debugLog("freed after orphan detected for @1000", true); + + } + + @Test + public void doGetRefCountInfoWithTrackRefsFalseAndTrackFreesTrue() { + rchi = getFalseTrue(); + long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + List<RefCountChangeInfo> list = rchi.getRefCountInfo(address); + assertEquals(null, list); + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); + assertEquals(null, info); + + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + rchi.refCountChanged(address, decRefCount, rc); // this will be ignored. + decRefCount = true; + rchi.refCountChanged(address, decRefCount, rc); // this will be ignored. + rchi.freeRefCountInfo(address); // this will be ignored. + + } + + @Test + public void doGetRefCountInfoWithTrackRefsFalseAndTrackFreesFalse() { + rchi = getFalseFalse(); + long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + List<RefCountChangeInfo> list = rchi.getRefCountInfo(address); + assertEquals(null, list); + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + + List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); + assertEquals(null, info); + + list = rchi.peekRefCountInfo(address); + assertEquals(null, list); + } + + @Test + public void doGetRefCountInfoWithTrackRefsTrueAndTrackFreesFalse() { + rchi = getTrueFalse(); + long address = (long) 0x1000; + boolean decRefCount = false; + int rc = 1; + + List<RefCountChangeInfo> list = null; + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + RefCountChangeInfo rcci = list.get(0); + assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + rchi.refCountChanged(address, decRefCount, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1 when adding new entry? + + List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo + assertEquals(1, info.size()); + rcci = info.get(0); + assertEquals(1, rcci.getUseCount()); + + list = rchi.peekRefCountInfo(address); + assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty) + } + + private ReferenceCountHelperImpl getTrueTrue() { + return new ReferenceCountHelperImpl(true, true); + } + + private ReferenceCountHelperImpl getTrueFalse() { + return new ReferenceCountHelperImpl(true, false); + } + + private ReferenceCountHelperImpl getFalseTrue() { + return new ReferenceCountHelperImpl(false, true); + } + + private ReferenceCountHelperImpl getFalseFalse() { + return new ReferenceCountHelperImpl(false, false); + } + + private ReferenceCountHelperImpl getHookedImpl() { + return new HookedReferenceCountHelperImpl(true, true); + } + + @Test + public void doGetRefCountInfoNonRegionEntryConcurrencyTest() { + rchi = getHookedImpl(); + long address = (long) 0x1000; + int rc = 1; + RefCountChangeInfo rcci; + + List<RefCountChangeInfo> list = null; + + rchi.setReferenceCountOwner("TestOwner"); // assume test identity + + rchi.refCountChanged(address, false, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // hooked impl simulates a concurrent update, so cnt is > expected + + rchi.setReferenceCountOwner(null); // sets owner to null and resets count + rchi.setReferenceCountOwner(null); // sets owner to null and resets count + + rchi.setReferenceCountOwner("TestOwner2"); // assume new identity + + rchi.refCountChanged(address, false, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(2, rcci.getUseCount()); // list is not null, so hook not used + + rchi.refCountChanged(address, true, rc); // dec ref count + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); // dec adds new list of stack traces + rcci = list.get(1); + assertEquals(0, rcci.getUseCount()); // cnt starts at 0 for new entries + + List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo + assertEquals(3, info.size()); // hooked impl added one to list + rcci = info.get(2); + assertEquals(0, rcci.getUseCount()); // count starts at 0 for new entries + + list = rchi.peekRefCountInfo(address); + assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty) + } + + @Test + public void doGetRefCountInfoRegionEntryConcurrencyTest() { + rchi = getHookedImpl(); + long address = (long) 0x1000; + int rc = 1; + RefCountChangeInfo rcci; + + List<RefCountChangeInfo> list = null; + + RegionEntry re = mock(RegionEntry.class); + rchi.setReferenceCountOwner(re); // set owner to region entry type + + rchi.refCountChanged(address, false, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(1, rcci.getUseCount()); // hooked impl simulates a concurrent update, so cnt is > expected + + rchi.setReferenceCountOwner(null); // sets owner to null and resets count + rchi.setReferenceCountOwner(null); // sets owner to null and resets count + + RegionEntry re2 = mock(RegionEntry.class); + rchi.setReferenceCountOwner(re2); // set owner to region entry type + + rchi.refCountChanged(address, false, rc); + list = rchi.peekRefCountInfo(address); + assertEquals(1, list.size()); + rcci = list.get(0); + assertEquals(2, rcci.getUseCount()); // list is not null, so hook not used + + rchi.refCountChanged(address, true, rc); // dec ref count + list = rchi.peekRefCountInfo(address); + assertEquals(2, list.size()); // dec adds new list of stack traces + rcci = list.get(1); + assertEquals(0, rcci.getUseCount()); // cnt starts at 0 for new entries + + List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo + assertEquals(3, info.size()); // hooked impl added one to list + rcci = info.get(2); + assertEquals(0, rcci.getUseCount()); // count starts at 0 for new entries + + list = rchi.peekRefCountInfo(address); + assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty) + } + + private class HookedReferenceCountHelperImpl extends ReferenceCountHelperImpl { + HookedReferenceCountHelperImpl(boolean trackRefCounts, boolean trackFreedRefCounts) { + super(trackRefCounts, trackFreedRefCounts); + } + + protected int refCountChangedTestHookCount = 0; + + /* + * Update list of stack traces for address. + * Hooked SUT should see that the list changed. + */ + @Override + protected void getReferenceCountInfoTestHook(ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces, long address) { + List<RefCountChangeInfo> updatedList = new ArrayList<RefCountChangeInfo>(stacktraces.get(address)); + RefCountChangeInfo rcci = new RefCountChangeInfo(false, 0, "TestOwner"); + updatedList.add(rcci); + stacktraces.put(address, updatedList); + } + /* + * Reinvoke refCountChanged to update reference count. + * Hooked SUT should see that the count has changed. + */ + @Override + protected void refCountChangedTestHook(Long address, boolean decRefCount, int rc) { + if(refCountChangedTestHookCount==0) { + refCountChangedTestHookCount++; + refCountChanged(address, decRefCount, rc); + } else { + refCountChangedTestHookCount--; + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/32a69873/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperJUnitTest.java b/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperJUnitTest.java new file mode 100644 index 0000000..3b38c8c --- /dev/null +++ b/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/ReferenceCountHelperJUnitTest.java @@ -0,0 +1,208 @@ +/* + * 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 com.gemstone.gemfire.internal.offheap; + +import static org.junit.Assert.*; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import com.gemstone.gemfire.test.junit.categories.UnitTest; + +import org.mockito.Mockito; +import static org.mockito.Mockito.*; + +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.*; + +/* + * This test simply verifies the static class delegates properly to the impl + * + * PowerMock used in this test to inject mocked impl into static class + * The PowerMockRule bootstraps PowerMock without the need for + * the @RunWith(PowerMockRunner.class) annotation, which was interfering with jacoco + * + */ +@Category(UnitTest.class) +@RunWith(PowerMockRunner.class) +@PowerMockIgnore({ "*.UnitTest" }) +@PrepareForTest({ ReferenceCountHelper.class }) +public class ReferenceCountHelperJUnitTest { + + private ReferenceCountHelperImpl prepareInstance() { + ReferenceCountHelperImpl rchi = mock(ReferenceCountHelperImpl.class); + PowerMockito.mockStatic(ReferenceCountHelper.class, Mockito.CALLS_REAL_METHODS); + PowerMockito.when(ReferenceCountHelper.getInstance()).thenReturn(rchi); + return rchi; + } + + @Test + public void trackReferenceCountsTrueTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + when(rchi.trackReferenceCounts()).thenReturn(true); + boolean b = ReferenceCountHelper.trackReferenceCounts(); + assertTrue(b); + verify(rchi).trackReferenceCounts(); + } + + @Test + public void trackReferenceCountsFalseTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + when(rchi.trackReferenceCounts()).thenReturn(false); + boolean b = ReferenceCountHelper.trackReferenceCounts(); + assertFalse(b); + verify(rchi).trackReferenceCounts(); + } + + @Test + public void trackFreedReferenceCountsTrueTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + when(rchi.trackFreedReferenceCounts()).thenReturn(true); + assertTrue(ReferenceCountHelper.trackFreedReferenceCounts()); + verify(rchi).trackFreedReferenceCounts(); + } + + @Test + public void trackFreedReferenceCountsFalseTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + when(rchi.trackFreedReferenceCounts()).thenReturn(false); + assertFalse(ReferenceCountHelper.trackFreedReferenceCounts()); + verify(rchi).trackFreedReferenceCounts(); + } + + @Test + public void setReferenceCountOwnerTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + Object theOwner = new Object(); + ReferenceCountHelper.setReferenceCountOwner(theOwner); + verify(rchi).setReferenceCountOwner(theOwner); + } + + @Test + public void createReferenceCountOwnerTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + Object expectedResult = new String("createReferenceCountOwner result"); + when(rchi.createReferenceCountOwner()).thenReturn(expectedResult); + Object s = ReferenceCountHelper.createReferenceCountOwner(); + assertEquals(s, expectedResult); + verify(rchi).createReferenceCountOwner(); + } + + @Test + public void skipRefCountTrackingTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + ReferenceCountHelper.skipRefCountTracking(); + verify(rchi).skipRefCountTracking(); + } + + @Test + public void isRefCountTrackingTrueTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + when(rchi.isRefCountTracking()).thenReturn(true); + boolean b = ReferenceCountHelper.isRefCountTracking(); + assertTrue(b); + verify(rchi).isRefCountTracking(); + } + + @Test + public void isRefCountTrackingFalseTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + when(rchi.isRefCountTracking()).thenReturn(false); + boolean b = ReferenceCountHelper.isRefCountTracking(); + assertFalse(b); + verify(rchi).isRefCountTracking(); + } + + @Test + public void unskipRefCountTrackingTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + ReferenceCountHelper.unskipRefCountTracking(); + verify(rchi).unskipRefCountTracking(); + } + + @Test + public void getRefCountInfoTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + List<RefCountChangeInfo> expectedResult = Collections.emptyList(); + when(rchi.getRefCountInfo((long)1000)).thenReturn(expectedResult); + List<RefCountChangeInfo> l = ReferenceCountHelper.getRefCountInfo(1000); + assertEquals(l, expectedResult); + verify(rchi).getRefCountInfo(1000); + } + + @Test + public void refCountChangedTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + ReferenceCountHelper.refCountChanged((long)1000, true, 4); + verify(rchi).refCountChanged((long)1000, true, 4); + } + + @Test + public void freeRefCountInfoTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + ReferenceCountHelper.freeRefCountInfo((long)1000); + verify(rchi).freeRefCountInfo((long)1000); + } + + @Test + public void getReferenceCountOwnerTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + Object expectedResult = new String("getReferenceCountOwner result"); + when(rchi.getReferenceCountOwner()).thenReturn(expectedResult); + Object o = ReferenceCountHelper.getReferenceCountOwner(); + assertEquals(o, expectedResult); + verify(rchi).getReferenceCountOwner(); + } + + @Test + public void getReenterCountTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + AtomicInteger expectedResult = new AtomicInteger(8); + when(rchi.getReenterCount()).thenReturn(expectedResult); + AtomicInteger ai = ReferenceCountHelper.getReenterCount(); + assertEquals(ai, expectedResult); + verify(rchi).getReenterCount(); + } + + @Test + public void getFreeRefCountInfoTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + List<RefCountChangeInfo> expectedResult = Collections.emptyList(); + when(rchi.getFreeRefCountInfo((long) 1000)).thenReturn(expectedResult); + List<RefCountChangeInfo> l = ReferenceCountHelper.getFreeRefCountInfo(1000); + assertEquals(l, expectedResult); + verify(rchi).getFreeRefCountInfo(1000); + } + + @Test + public void peekRefCountInfoTest() throws Exception { + ReferenceCountHelperImpl rchi = prepareInstance(); + List<RefCountChangeInfo> expectedResult = Collections.emptyList(); + when(rchi.peekRefCountInfo((long) 1000)).thenReturn(expectedResult); + List<RefCountChangeInfo> l = ReferenceCountHelper.peekRefCountInfo(1000); + assertEquals(l, expectedResult); + verify(rchi).peekRefCountInfo(1000); + } +} \ No newline at end of file