GEODE-8531: Fix coredump in MapSegment::remove (#667)
- Fixed a coredump in MapSegment::remove whenever the the region holding the
MapSegment object had consistency checks disabled.
- Added an integration test in RegisterKeysTest TS as it was were this issue
was detected. Test approach is to setup a g-mocked cache listener for the listener region and set
a call expectation on afterDestroy. All of that giving a timeout of 5seconds for the call to be completed.
- With this approach the complete 5 seconds will be only spent in the event of an error.
- Also linked gmock to the new integration tests target.
diff --git a/cppcache/integration/test/CMakeLists.txt b/cppcache/integration/test/CMakeLists.txt
index be68ea1..fd6ebc5 100644
--- a/cppcache/integration/test/CMakeLists.txt
+++ b/cppcache/integration/test/CMakeLists.txt
@@ -70,6 +70,7 @@
ACE::ACE
GTest::gtest
GTest::gtest_main
+ GTest::gmock
Boost::boost
Boost::system
Boost::log
diff --git a/cppcache/integration/test/RegisterKeysTest.cpp b/cppcache/integration/test/RegisterKeysTest.cpp
index 58eb113..0c1a6fc 100644
--- a/cppcache/integration/test/RegisterKeysTest.cpp
+++ b/cppcache/integration/test/RegisterKeysTest.cpp
@@ -14,13 +14,16 @@
* limitations under the License.
*/
-#include <iostream>
+#include <gmock/gmock.h>
+
+#include <condition_variable>
+#include <mutex>
#include <gtest/gtest.h>
#include <geode/Cache.hpp>
#include <geode/CacheFactory.hpp>
-#include <geode/PoolManager.hpp>
+#include <geode/EntryEvent.hpp>
#include <geode/RegionFactory.hpp>
#include <geode/RegionShortcut.hpp>
@@ -28,6 +31,12 @@
#include "framework/Framework.h"
#include "framework/Gfsh.h"
+class CacheListenerMock : public apache::geode::client::CacheListener {
+ public:
+ MOCK_METHOD1(afterDestroy,
+ void(const apache::geode::client::EntryEvent& event));
+};
+
namespace {
using apache::geode::client::Cache;
@@ -38,6 +47,9 @@
using apache::geode::client::IllegalStateException;
using apache::geode::client::Region;
using apache::geode::client::RegionShortcut;
+using ::testing::_;
+
+ACTION_P(CvNotifyOne, cv) { cv->notify_one(); }
Cache createTestCache() {
CacheFactory cacheFactory;
@@ -46,9 +58,11 @@
.create();
}
-std::shared_ptr<Region> setupCachingProxyRegion(Cache& cache) {
+std::shared_ptr<Region> setupCachingProxyRegion(Cache& cache,
+ bool consistency = true) {
auto region = cache.createRegionFactory(RegionShortcut::CACHING_PROXY)
.setPoolName("default")
+ .setConcurrencyChecksEnabled(consistency)
.create("region");
return region;
@@ -112,6 +126,60 @@
}
}
+TEST(RegisterKeysTest, RegisterAllWithConsistencyDisabled) {
+ Cluster cluster{LocatorCount{1}, ServerCount{1}};
+
+ cluster.start();
+
+ cluster.getGfsh()
+ .create()
+ .region()
+ .withName("region")
+ .withType("PARTITION")
+ .execute();
+
+ auto producer_cache = createTestCache();
+ auto listener_cache = createTestCache();
+ std::shared_ptr<Region> producer_region;
+ std::shared_ptr<Region> listener_region;
+
+ {
+ auto poolFactory = producer_cache.getPoolManager().createFactory();
+ cluster.applyLocators(poolFactory);
+ poolFactory.create("default");
+ producer_region = setupProxyRegion(producer_cache);
+ }
+
+ auto listener = std::make_shared<CacheListenerMock>();
+ {
+ auto poolFactory =
+ listener_cache.getPoolManager().createFactory().setSubscriptionEnabled(
+ true);
+ cluster.applyLocators(poolFactory);
+ poolFactory.create("default");
+ listener_region =
+ listener_cache.createRegionFactory(RegionShortcut::CACHING_PROXY)
+ .setPoolName("default")
+ .setCacheListener(listener)
+ .setConcurrencyChecksEnabled(false)
+ .create("region");
+ listener_region->registerAllKeys();
+ }
+
+ producer_region->put("one", std::make_shared<CacheableInt16>(1));
+ producer_region->destroy("one");
+
+ std::mutex cv_mutex;
+ std::condition_variable cv;
+ EXPECT_CALL(*listener, afterDestroy(_)).Times(1).WillOnce(CvNotifyOne(&cv));
+
+ {
+ std::unique_lock<std::mutex> lock(cv_mutex);
+ EXPECT_EQ(cv.wait_for(lock, std::chrono::seconds(5)),
+ std::cv_status::no_timeout);
+ }
+}
+
TEST(RegisterKeysTest, RegisterAnyWithCachingRegion) {
Cluster cluster{LocatorCount{1}, ServerCount{1}};
diff --git a/cppcache/src/MapSegment.cpp b/cppcache/src/MapSegment.cpp
index ce88114..36ef62f 100644
--- a/cppcache/src/MapSegment.cpp
+++ b/cppcache/src/MapSegment.cpp
@@ -332,7 +332,6 @@
std::shared_ptr<MapEntryImpl>& me, int updateCount,
std::shared_ptr<VersionTag> versionTag,
bool afterRemote, bool& isEntryFound) {
- std::shared_ptr<MapEntry> entry;
if (m_concurrencyChecksEnabled) {
TombstoneExpiryHandler* handler;
auto id = m_tombstoneList->getExpiryTask(&handler);
@@ -353,7 +352,9 @@
}
std::lock_guard<decltype(m_spinlock)> lk(m_spinlock);
- if (m_map->erase(key) == 0) {
+ auto&& iter = m_map->find(key);
+
+ if (iter == m_map->end()) {
// didn't unbind, probably no entry...
oldValue = nullptr;
volatile int destroyTrackers = *m_numDestroyTrackers;
@@ -363,16 +364,21 @@
return GF_CACHE_ENTRY_NOT_FOUND;
}
+ auto entry = iter->second;
+ m_map->erase(iter);
+
if (updateCount >= 0 && updateCount != entry->getUpdateCount()) {
// this is the case when entry has been updated while being tracked
return GF_CACHE_ENTRY_UPDATED;
}
+
auto entryImpl = entry->getImplPtr();
entryImpl->getValueI(oldValue);
if (CacheableToken::isTombstone(oldValue)) oldValue = nullptr;
if (oldValue) {
me = entryImpl;
}
+
return GF_NOERR;
}