Repository: incubator-geode
Updated Branches:
  refs/heads/develop 8341d7783 -> b7518411d


GEODE-1886: optimize off-heap reference checks

EntryEventImpl now tests the region's off-heap boolean
before it will ask if the new/old value is an instanceof
StoredObject.


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/b7518411
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/b7518411
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/b7518411

Branch: refs/heads/develop
Commit: b7518411d93ded8a8c83af6964ff74acb04a31ac
Parents: 8341d77
Author: Darrel Schneider <dschnei...@pivotal.io>
Authored: Mon Sep 12 16:45:59 2016 -0700
Committer: Darrel Schneider <dschnei...@pivotal.io>
Committed: Fri Sep 16 11:14:33 2016 -0700

----------------------------------------------------------------------
 .../geode/internal/cache/EntryEventImpl.java    | 92 +++++++++++++-------
 .../geode/internal/offheap/StoredObject.java    |  6 ++
 .../internal/cache/EntryEventImplTest.java      |  9 ++
 .../cache/SearchLoadAndWriteProcessorTest.java  |  1 +
 4 files changed, 78 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b7518411/geode-core/src/main/java/org/apache/geode/internal/cache/EntryEventImpl.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/cache/EntryEventImpl.java 
b/geode-core/src/main/java/org/apache/geode/internal/cache/EntryEventImpl.java
index a555cca..6a964c0 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/cache/EntryEventImpl.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/cache/EntryEventImpl.java
@@ -770,7 +770,11 @@ public class EntryEventImpl
    * Note: to prevent the heap copy use getRawNewValue instead
    */
   public final Object getRawNewValueAsHeapObject() {
-    return 
OffHeapHelper.getHeapForm(OffHeapHelper.copyIfNeeded(basicGetNewValue()));
+    Object result = basicGetNewValue();
+    if (mayHaveOffHeapReferences()) {
+      result = OffHeapHelper.copyIfNeeded(result);
+    }
+    return result;
   }
   
   /**
@@ -791,27 +795,23 @@ public class EntryEventImpl
   @Released(ENTRY_EVENT_NEW_VALUE)
   protected void basicSetNewValue(@Retained(ENTRY_EVENT_NEW_VALUE) Object v) {
     if (v == this.newValue) return;
-    if (this.offHeapOk) {
-      OffHeapHelper.releaseAndTrackOwner(this.newValue, this);
-    }
-    if (isOffHeapReference(v)) {
-      ReferenceCountHelper.setReferenceCountOwner(this);
-      if (!((StoredObject) v).retain()) {
+    if (mayHaveOffHeapReferences()) {
+      if (this.offHeapOk) {
+        OffHeapHelper.releaseAndTrackOwner(this.newValue, this);
+      }
+      if (StoredObject.isOffHeapReference(v)) {
+        ReferenceCountHelper.setReferenceCountOwner(this);
+        if (!((StoredObject) v).retain()) {
+          ReferenceCountHelper.setReferenceCountOwner(null);
+          this.newValue = null;
+          return;
+        }
         ReferenceCountHelper.setReferenceCountOwner(null);
-        this.newValue = null;
-        return;
       }
-      ReferenceCountHelper.setReferenceCountOwner(null);
     }
     this.newValue = v;
     this.cachedSerializedNewValue = null;
   }
-  /**
-   * Returns true if this event has a reference to an off-heap new or old 
value.
-   */
-  public boolean hasOffHeapValue() {
-    return isOffHeapReference(this.newValue) || 
isOffHeapReference(this.oldValue);
-  }
   
   @Unretained
   protected final Object basicGetNewValue() {
@@ -823,8 +823,8 @@ public class EntryEventImpl
     return result;
   }
   
-  private static boolean isOffHeapReference(Object ref) {
-    return (ref instanceof StoredObject) && ((StoredObject)ref).hasRefCount();
+  private boolean isOffHeapReference(Object ref) {
+    return mayHaveOffHeapReferences() && StoredObject.isOffHeapReference(ref);
   }
   
   private class OldValueOwner {
@@ -859,7 +859,7 @@ public class EntryEventImpl
   private void basicSetOldValue(@Unretained(ENTRY_EVENT_OLD_VALUE) Object v) {
     @Released final Object curOldValue = this.oldValue;
     if (v == curOldValue) return;
-    if (this.offHeapOk) {
+    if (this.offHeapOk && mayHaveOffHeapReferences()) {
       if (ReferenceCountHelper.trackReferenceCounts()) {
         OffHeapHelper.releaseAndTrackOwner(curOldValue, new OldValueOwner());
       } else {
@@ -910,7 +910,11 @@ public class EntryEventImpl
    * To avoid the heap copy use getRawOldValue instead.
    */
   public final Object getRawOldValueAsHeapObject() {
-    return 
OffHeapHelper.getHeapForm(OffHeapHelper.copyIfNeeded(basicGetOldValue()));
+    Object result = basicGetOldValue();
+    if (mayHaveOffHeapReferences()) {
+      result = OffHeapHelper.copyIfNeeded(result);
+    }
+    return result;
   }
   /*
    * If the old value is off-heap return the StoredObject form (unretained 
OFF_HEAP_REFERENCE). 
@@ -927,7 +931,7 @@ public class EntryEventImpl
   @Unretained(ENTRY_EVENT_OLD_VALUE)
   public final Object getOldValueAsOffHeapDeserializedOrRaw() {
     Object result = basicGetOldValue();
-    if (result instanceof StoredObject) {
+    if (mayHaveOffHeapReferences() && result instanceof StoredObject) {
       result = ((StoredObject) result).getDeserializedForReading();
     }
     return AbstractRegion.handleNotAvailable(result); // fixes 49499
@@ -1290,7 +1294,7 @@ public class EntryEventImpl
   @Unretained(ENTRY_EVENT_NEW_VALUE)
   public final Object getNewValueAsOffHeapDeserializedOrRaw() {
     Object result = getRawNewValue();
-    if (result instanceof StoredObject) {
+    if (mayHaveOffHeapReferences() && result instanceof StoredObject) {
       result = ((StoredObject) result).getDeserializedForReading();
     }
     return AbstractRegion.handleNotAvailable(result); // fixes 49499
@@ -1314,7 +1318,10 @@ public class EntryEventImpl
     return convertToStoredObject(basicGetOldValue());
   }
 
-  private static StoredObject convertToStoredObject(final Object tmp) {
+  private StoredObject convertToStoredObject(final Object tmp) {
+    if (!mayHaveOffHeapReferences()) {
+      return null;
+    }
     if (!(tmp instanceof StoredObject)) {
       return null;
     }
@@ -1780,7 +1787,11 @@ public class EntryEventImpl
     if (this.op != Operation.LOCAL_INVALIDATE
         && this.op != Operation.LOCAL_DESTROY) {
       // fix for bug 34387
-      tx.setPendingValue(OffHeapHelper.copyIfNeeded(v));
+      Object pv = v;
+      if (mayHaveOffHeapReferences()) {
+        pv = OffHeapHelper.copyIfNeeded(v);
+      }
+      tx.setPendingValue(pv);
     }
     tx.setCallbackArgument(getCallbackArgument());
   }
@@ -1797,7 +1808,9 @@ public class EntryEventImpl
       try {
         return setOldValue(v);
       } finally {
-        OffHeapHelper.releaseWithNoTracking(v);
+        if (mayHaveOffHeapReferences()) {
+          OffHeapHelper.releaseWithNoTracking(v);
+        }
       }
     }
     catch (EntryNotFoundException ex) {
@@ -2557,7 +2570,7 @@ public class EntryEventImpl
     private final byte[] serializedValue;
     
     SerializedCacheValueImpl(EntryEventImpl event, Region r, RegionEntry re, 
@Unretained CachedDeserializable cd, byte[] serializedBytes) {
-      if (isOffHeapReference(cd)) {
+      if (event.isOffHeapReference(cd)) {
         this.event = event;
       } else {
         this.event = null;
@@ -2748,6 +2761,9 @@ public class EntryEventImpl
   public void release() {
     // noop if already freed or values can not be off-heap
     if (!this.offHeapOk) return;
+    if (!mayHaveOffHeapReferences()) {
+      return;
+    }
     synchronized (this.offHeapLock) {
       // Note that this method does not set the old/new values to null but
       // leaves them set to the off-heap value so that future calls to 
getOld/NewValue
@@ -2771,6 +2787,18 @@ public class EntryEventImpl
     }
   }
   
+  /**
+   * Return true if this EntryEvent may have off-heap references.
+   */
+  private boolean mayHaveOffHeapReferences() {
+    LocalRegion lr = this.region;
+    if (lr != null) {
+      return lr.getOffHeap();
+    }
+    // if region field is null it is possible that we have off-heap values
+    return true;
+  }
+
   void testHookReleaseInProgress() {
     // unit test can mock or override this method
   }
@@ -2781,7 +2809,7 @@ public class EntryEventImpl
    */
   public void disallowOffHeapValues() {
     if (isOffHeapReference(this.newValue) || 
isOffHeapReference(this.oldValue)) {
-      throw new IllegalStateException("This event does not support off-heap 
values");
+      throw new IllegalStateException("This event already has off-heap 
values");
     }
     synchronized (this.offHeapLock) {
       this.offHeapOk = false;
@@ -2794,9 +2822,13 @@ public class EntryEventImpl
    */
   @Released({ENTRY_EVENT_NEW_VALUE, ENTRY_EVENT_OLD_VALUE})
   public void copyOffHeapToHeap() {
+    if (!mayHaveOffHeapReferences()) {
+      this.offHeapOk = false;
+      return;
+    }
     synchronized (this.offHeapLock) {
       Object ov = basicGetOldValue();
-      if (isOffHeapReference(ov)) {
+      if (StoredObject.isOffHeapReference(ov)) {
         if (ReferenceCountHelper.trackReferenceCounts()) {
           ReferenceCountHelper.setReferenceCountOwner(new OldValueOwner());
           this.oldValue = OffHeapHelper.copyAndReleaseIfNeeded(ov);
@@ -2806,12 +2838,12 @@ public class EntryEventImpl
         }
       }
       Object nv = basicGetNewValue();
-      if (isOffHeapReference(nv)) {
+      if (StoredObject.isOffHeapReference(nv)) {
         ReferenceCountHelper.setReferenceCountOwner(this);
         this.newValue = OffHeapHelper.copyAndReleaseIfNeeded(nv);
         ReferenceCountHelper.setReferenceCountOwner(null);
       }
-      if (isOffHeapReference(this.newValue) || 
isOffHeapReference(this.oldValue)) {
+      if (StoredObject.isOffHeapReference(this.newValue) || 
StoredObject.isOffHeapReference(this.oldValue)) {
         throw new IllegalStateException("event's old/new value still off-heap 
after calling copyOffHeapToHeap");
       }
       this.offHeapOk = false;

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b7518411/geode-core/src/main/java/org/apache/geode/internal/offheap/StoredObject.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/offheap/StoredObject.java 
b/geode-core/src/main/java/org/apache/geode/internal/offheap/StoredObject.java
index 4fd12ef..87e2ca5 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/offheap/StoredObject.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/offheap/StoredObject.java
@@ -170,4 +170,10 @@ public interface StoredObject extends Sendable, 
CachedDeserializable, Releasable
    */
   public StoredObject getStoredObjectWithoutHeapForm();
 
+  /**
+   * Return true if the given "o" is reference to off-heap memory.
+   */
+  public static boolean isOffHeapReference(Object o) {
+    return (o instanceof StoredObject) && ((StoredObject)o).hasRefCount();
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b7518411/geode-core/src/test/java/org/apache/geode/internal/cache/EntryEventImplTest.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/test/java/org/apache/geode/internal/cache/EntryEventImplTest.java
 
b/geode-core/src/test/java/org/apache/geode/internal/cache/EntryEventImplTest.java
index 973787c..e2b47d5 100644
--- 
a/geode-core/src/test/java/org/apache/geode/internal/cache/EntryEventImplTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/internal/cache/EntryEventImplTest.java
@@ -68,6 +68,7 @@ public class EntryEventImplTest {
   @Test
   public void verifyExportNewValueWithUnserializedStoredObject() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject newValue = mock(StoredObject.class);
     byte[] newValueBytes = new byte[]{1,2,3};
     when(newValue.getValueAsHeapByteArray()).thenReturn(newValueBytes);
@@ -174,6 +175,7 @@ public class EntryEventImplTest {
   @Test
   public void 
verifyExportNewValueWithSerializedStoredObjectAndImporterPrefersSerialized() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject newValue = mock(StoredObject.class);
     when(newValue.isSerialized()).thenReturn(true);
     byte[] newValueBytes = new byte[]{1,2,3};
@@ -190,6 +192,7 @@ public class EntryEventImplTest {
   @Test
   public void verifyExportNewValueWithSerializedStoredObject() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject newValue = mock(StoredObject.class);
     when(newValue.isSerialized()).thenReturn(true);
     Object newValueDeserialized = "newValueDeserialized";
@@ -205,6 +208,7 @@ public class EntryEventImplTest {
   @Test
   public void 
verifyExportNewValueWithSerializedStoredObjectAndUnretainedNewReferenceOk() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject newValue = mock(StoredObject.class);
     when(newValue.isSerialized()).thenReturn(true);
     Object newValueDeserialized = "newValueDeserialized";
@@ -221,6 +225,7 @@ public class EntryEventImplTest {
   @Test
   public void verifyExportOldValueWithUnserializedStoredObject() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject oldValue = mock(StoredObject.class);
     byte[] oldValueBytes = new byte[]{1,2,3};
     when(oldValue.getValueAsHeapByteArray()).thenReturn(oldValueBytes);
@@ -325,6 +330,7 @@ public class EntryEventImplTest {
   @Test
   public void 
verifyExportOldValueWithSerializedStoredObjectAndImporterPrefersSerialized() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject oldValue = mock(StoredObject.class);
     when(oldValue.isSerialized()).thenReturn(true);
     byte[] oldValueBytes = new byte[]{1,2,3};
@@ -342,6 +348,7 @@ public class EntryEventImplTest {
   @Test
   public void verifyExportOldValueWithSerializedStoredObject() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject oldValue = mock(StoredObject.class);
     when(oldValue.isSerialized()).thenReturn(true);
     Object oldValueDeserialized = "newValueDeserialized";
@@ -358,6 +365,7 @@ public class EntryEventImplTest {
   @Test
   public void 
verifyExportOldValueWithSerializedStoredObjectAndUnretainedOldReferenceOk() {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject oldValue = mock(StoredObject.class);
     when(oldValue.isSerialized()).thenReturn(true);
     Object oldValueDeserialized = "oldValueDeserialized";
@@ -375,6 +383,7 @@ public class EntryEventImplTest {
   @Test
   public void verifyExternalReadMethodsBlockedByRelease() throws 
InterruptedException {
     LocalRegion region = mock(LocalRegion.class);
+    when(region.getOffHeap()).thenReturn(true);
     StoredObject newValue = mock(StoredObject.class);
     when(newValue.hasRefCount()).thenReturn(true);
     when(newValue.isSerialized()).thenReturn(true);

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b7518411/geode-core/src/test/java/org/apache/geode/internal/cache/SearchLoadAndWriteProcessorTest.java
----------------------------------------------------------------------
diff --git 
a/geode-core/src/test/java/org/apache/geode/internal/cache/SearchLoadAndWriteProcessorTest.java
 
b/geode-core/src/test/java/org/apache/geode/internal/cache/SearchLoadAndWriteProcessorTest.java
index 84c6652..135e27f 100644
--- 
a/geode-core/src/test/java/org/apache/geode/internal/cache/SearchLoadAndWriteProcessorTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/internal/cache/SearchLoadAndWriteProcessorTest.java
@@ -40,6 +40,7 @@ public class SearchLoadAndWriteProcessorTest {
     // setup
     SearchLoadAndWriteProcessor processor = 
SearchLoadAndWriteProcessor.getProcessor();
     LocalRegion lr = mock(LocalRegion.class);
+    when(lr.getOffHeap()).thenReturn(true);
     when(lr.getScope()).thenReturn(Scope.DISTRIBUTED_ACK);
     Object key = "key";
     StoredObject value = mock(StoredObject.class);

Reply via email to