This is an automated email from the ASF dual-hosted git repository.

bbender pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-native.git


The following commit(s) were added to refs/heads/develop by this push:
     new bb40163  GEODE-7299: Fix PdxInstanceImpl memory leak (#537)
bb40163 is described below

commit bb40163f5eb900b0ca18512d619e17ca88d13c65
Author: Blake Bender <[email protected]>
AuthorDate: Thu Oct 17 18:47:11 2019 +0000

    GEODE-7299: Fix PdxInstanceImpl memory leak (#537)
    
    * GEODE-7299: Fix memory leak in PdxInstanceImpl::updatePdxStream
    - include a unit test which will show the leak when run under leak-checking 
app, e.g. Valgrind
    - m_buffer member variable changed to a std::vector
    - m_bufferLength member variable no longer needed, removed
    
    Co-authored-by: Mike Martell <[email protected]>
---
 cppcache/src/PdxInstanceImpl.cpp      | 59 +++++++++++++++--------------
 cppcache/src/PdxInstanceImpl.hpp      |  5 +--
 cppcache/test/PdxInstanceImplTest.cpp | 70 +++++++++++++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 33 deletions(-)

diff --git a/cppcache/src/PdxInstanceImpl.cpp b/cppcache/src/PdxInstanceImpl.cpp
index 0796320..2bfa8d4 100644
--- a/cppcache/src/PdxInstanceImpl.cpp
+++ b/cppcache/src/PdxInstanceImpl.cpp
@@ -59,24 +59,21 @@ bool sortFunc(std::shared_ptr<PdxFieldType> field1,
   }
 }
 
-PdxInstanceImpl::~PdxInstanceImpl() noexcept {
-  _GEODE_SAFE_DELETE_ARRAY(m_buffer);
-}
+PdxInstanceImpl::~PdxInstanceImpl() noexcept {}
 
-PdxInstanceImpl::PdxInstanceImpl(uint8_t* buffer, int length, int typeId,
-                                 CachePerfStats& cacheStats,
+PdxInstanceImpl::PdxInstanceImpl(const uint8_t* buffer, size_t length,
+                                 int typeId, CachePerfStats& cacheStats,
                                  PdxTypeRegistry& pdxTypeRegistry,
                                  const CacheImpl& cacheImpl,
                                  bool enableTimeStatistics)
-    : m_buffer(DataInput::getBufferCopy(buffer, length)),
-      m_bufferLength(length),
+    : m_buffer(buffer, buffer + length),
       m_typeId(typeId),
       m_pdxType(nullptr),
       m_cacheStats(cacheStats),
       m_pdxTypeRegistry(pdxTypeRegistry),
       m_cacheImpl(cacheImpl),
       m_enableTimeStatistics(enableTimeStatistics) {
-  LOGDEBUG("PdxInstanceImpl::m_bufferLength = %d ", m_bufferLength);
+  LOGDEBUG("PdxInstanceImpl::m_bufferLength = %zu ", m_buffer.size());
 }
 
 PdxInstanceImpl::PdxInstanceImpl(FieldVsValues fieldVsValue,
@@ -85,9 +82,7 @@ PdxInstanceImpl::PdxInstanceImpl(FieldVsValues fieldVsValue,
                                  PdxTypeRegistry& pdxTypeRegistry,
                                  const CacheImpl& cacheImpl,
                                  bool enableTimeStatistics)
-    : m_buffer(nullptr),
-      m_bufferLength(0),
-      m_typeId(0),
+    : m_typeId(0),
       m_pdxType(pdxType),
       m_updatedFields(fieldVsValue),
       m_cacheStats(cacheStats),
@@ -253,11 +248,11 @@ void PdxInstanceImpl::writeField(PdxWriter& writer,
   }
 }
 std::shared_ptr<WritablePdxInstance> PdxInstanceImpl::createWriter() {
-  LOGDEBUG("PdxInstanceImpl::createWriter m_bufferLength = %d m_typeId = %d ",
-           m_bufferLength, m_typeId);
+  LOGDEBUG("PdxInstanceImpl::createWriter m_bufferLength = %zu m_typeId = %d ",
+           m_buffer.size(), m_typeId);
   return std::make_shared<PdxInstanceImpl>(
-      m_buffer, m_bufferLength, m_typeId, m_cacheStats, m_pdxTypeRegistry,
-      m_cacheImpl,
+      m_buffer.data(), m_buffer.size(), m_typeId, m_cacheStats,
+      m_pdxTypeRegistry, m_cacheImpl,
       m_enableTimeStatistics);  // need to create duplicate byte stream);
 }
 
@@ -647,7 +642,8 @@ int32_t PdxInstanceImpl::hashcode() const {
 
   auto pdxIdentityFieldList = getIdentityPdxFields(pt);
 
-  auto dataInput = m_cacheImpl.createDataInput(m_buffer, m_bufferLength);
+  auto dataInput =
+      m_cacheImpl.createDataInput(m_buffer.data(), m_buffer.size());
 
   for (uint32_t i = 0; i < pdxIdentityFieldList.size(); i++) {
     auto pField = pdxIdentityFieldList.at(i);
@@ -709,9 +705,10 @@ int32_t PdxInstanceImpl::hashcode() const {
 }
 
 void PdxInstanceImpl::updatePdxStream(uint8_t* newPdxStream, int len) {
-  m_buffer = DataInput::getBufferCopy(newPdxStream, len);
-  m_bufferLength = len;
+  m_buffer.resize(len);
+  memcpy(m_buffer.data(), newPdxStream, len);
 }
+
 std::shared_ptr<PdxType> PdxInstanceImpl::getPdxType() const {
   if (m_typeId == 0) {
     if (m_pdxType == nullptr) {
@@ -1060,11 +1057,13 @@ std::string PdxInstanceImpl::toString() const {
 }
 
 std::shared_ptr<PdxSerializable> PdxInstanceImpl::getObject() {
-  auto dataInput = m_cacheImpl.createDataInput(m_buffer, m_bufferLength);
+  auto dataInput =
+      m_cacheImpl.createDataInput(m_buffer.data(), m_buffer.size());
   int64_t sampleStartNanos =
       m_enableTimeStatistics ? Utils::startStatOpTime() : 0;
   //[ToDo] do we have to call incPdxDeSerialization here?
-  auto ret = PdxHelper::deserializePdx(dataInput, m_typeId, m_bufferLength);
+  auto ret = PdxHelper::deserializePdx(dataInput, m_typeId,
+                                       static_cast<int32_t>(m_buffer.size()));
 
   if (m_enableTimeStatistics) {
     Utils::updateStatOpTime(m_cacheStats.getStat(),
@@ -1133,9 +1132,10 @@ bool PdxInstanceImpl::operator==(const CacheableKey& 
other) const {
   equatePdxFields(myPdxIdentityFieldList, otherPdxIdentityFieldList);
   equatePdxFields(otherPdxIdentityFieldList, myPdxIdentityFieldList);
 
-  auto myDataInput = m_cacheImpl.createDataInput(m_buffer, m_bufferLength);
-  auto otherDataInput =
-      m_cacheImpl.createDataInput(otherPdx->m_buffer, 
otherPdx->m_bufferLength);
+  auto myDataInput =
+      m_cacheImpl.createDataInput(m_buffer.data(), m_buffer.size());
+  auto otherDataInput = m_cacheImpl.createDataInput(otherPdx->m_buffer.data(),
+                                                    otherPdx->m_buffer.size());
 
   PdxFieldTypes fieldTypeId;
   for (size_t i = 0; i < myPdxIdentityFieldList.size(); i++) {
@@ -1334,10 +1334,9 @@ void PdxInstanceImpl::toDataMutable(PdxWriter& writer) {
       pt->getPdxFieldTypes();
   int position = 0;  // ignore typeid and length
   int nextFieldPosition = 0;
-  if (m_buffer != nullptr) {
-    uint8_t* copy = apache::geode::client::DataInput::getBufferCopy(
-        m_buffer, m_bufferLength);
-    auto dataInput = m_cacheImpl.createDataInput(copy, m_bufferLength);
+  if (m_buffer.size() != 0) {
+    auto dataInput =
+        m_cacheImpl.createDataInput(m_buffer.data(), m_buffer.size());
     for (size_t i = 0; i < pdxFieldList->size(); i++) {
       auto currPf = pdxFieldList->at(i);
       LOGDEBUG("toData filedname = %s , isVarLengthType = %d ",
@@ -1366,7 +1365,6 @@ void PdxInstanceImpl::toDataMutable(PdxWriter& writer) {
         position = nextFieldPosition;  // mark next field;
       }
     }
-    _GEODE_SAFE_DELETE_ARRAY(copy);
   } else {
     for (size_t i = 0; i < pdxFieldList->size(); i++) {
       auto currPf = pdxFieldList->at(i);
@@ -1946,7 +1944,7 @@ void PdxInstanceImpl::setOffsetForObject(DataInput& 
dataInput,
 
 size_t PdxInstanceImpl::objectSize() const {
   auto size = sizeof(PdxInstanceImpl);
-  size += m_bufferLength;
+  size += m_buffer.size();
   size += m_pdxType->objectSize();
   for (FieldVsValues::const_iterator iter = m_updatedFields.begin();
        iter != m_updatedFields.end(); ++iter) {
@@ -1969,7 +1967,8 @@ DataInput PdxInstanceImpl::getDataInputForField(
     throw IllegalStateException("PdxInstance doesn't have field " + fieldname);
   }
 
-  auto dataInput = m_cacheImpl.createDataInput(m_buffer, m_bufferLength);
+  auto dataInput =
+      m_cacheImpl.createDataInput(m_buffer.data(), m_buffer.size());
   auto pos = getOffset(dataInput, pt, pft->getSequenceId());
 
   dataInput.reset();
diff --git a/cppcache/src/PdxInstanceImpl.hpp b/cppcache/src/PdxInstanceImpl.hpp
index b9b3cc0..4f527d2 100644
--- a/cppcache/src/PdxInstanceImpl.hpp
+++ b/cppcache/src/PdxInstanceImpl.hpp
@@ -199,7 +199,7 @@ class APACHE_GEODE_EXPORT PdxInstanceImpl : public 
WritablePdxInstance {
    * @brief constructors
    */
 
-  PdxInstanceImpl(uint8_t* buffer, int length, int typeId,
+  PdxInstanceImpl(const uint8_t* buffer, size_t length, int typeId,
                   CachePerfStats& cacheStats, PdxTypeRegistry& pdxTypeRegistry,
                   const CacheImpl& cacheImpl, bool enableTimeStatistics);
 
@@ -216,8 +216,7 @@ class APACHE_GEODE_EXPORT PdxInstanceImpl : public 
WritablePdxInstance {
   void updatePdxStream(uint8_t* newPdxStream, int len);
 
  private:
-  uint8_t* m_buffer;
-  int m_bufferLength;
+  std::vector<uint8_t> m_buffer;
   int m_typeId;
   std::shared_ptr<PdxType> m_pdxType;
   FieldVsValues m_updatedFields;
diff --git a/cppcache/test/PdxInstanceImplTest.cpp 
b/cppcache/test/PdxInstanceImplTest.cpp
new file mode 100644
index 0000000..604b46f
--- /dev/null
+++ b/cppcache/test/PdxInstanceImplTest.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#include <CachePerfStats.hpp>
+#include <PdxInstanceImpl.hpp>
+#include <statistics/StatisticsFactory.hpp>
+
+#include <gtest/gtest.h>
+
+#include <geode/AuthenticatedView.hpp>
+#include <geode/Cache.hpp>
+#include <geode/PoolManager.hpp>
+#include <geode/Properties.hpp>
+#include <geode/RegionFactory.hpp>
+#include <geode/RegionShortcut.hpp>
+
+using apache::geode::client::Cache;
+using apache::geode::client::CacheFactory;
+using apache::geode::client::CacheImpl;
+using apache::geode::client::CachePerfStats;
+using apache::geode::client::PdxInstanceImpl;
+using apache::geode::client::Properties;
+using apache::geode::statistics::StatisticsFactory;
+
+#define __1K__ 1024
+#define __100K__ (100 * __1K__)
+#define __1M__ (__1K__ * __1K__)
+
+//
+// Test to check for memory leak in PdxInstanceImpl::updatePdxStream.  This
+// method was leaking a buffer equivalent in size to the passed-in buffer on
+// each call.  Test will still pass without the fix in updatePdxStream, but it
+// will take *much* longer to complete (60sec+ vs ~4sec on a typical machine).
+// Still, to actually verify the fix, you will have to run this test under a
+// memory-checking framework of some kind, such as Valgrind or XCode's
+// Instruments.
+//
+TEST(PdxInstanceImplTest, updatePdxStream) {
+  auto properties = std::make_shared<Properties>();
+  CacheFactory cacheFactory;
+  auto cache = cacheFactory.create();
+  CacheImpl cacheImpl(&cache, properties, true, false, nullptr);
+  auto buffer = std::vector<uint8_t>(__1M__, 0xcc);
+  auto len = static_cast<int32_t>(buffer.size());
+  PdxInstanceImpl pdxInstanceImpl(
+      buffer.data(), len, 0xdeadbeef, cacheImpl.getCachePerfStats(),
+      *(cacheImpl.getPdxTypeRegistry()), cacheImpl, false);
+
+  for (auto i = 0; i < __100K__; i++) {
+    try {
+      pdxInstanceImpl.updatePdxStream(buffer.data(), len);
+    } catch (const std::exception&) {
+      GTEST_FAIL();
+    }
+  }
+}

Reply via email to