diff --git a/envoy/stats/stats.h b/envoy/stats/stats.h index 10af2c396620..1b0ec8cfe28e 100644 --- a/envoy/stats/stats.h +++ b/envoy/stats/stats.h @@ -231,9 +231,20 @@ Envoy */ template class DeferredCreationCompatibleInterface { public: - // Helper function to get-or-create and return the StatsStructType object. + /** + * Returns an already existing StatsStructType, or creates a new one and returns it. + * + * @return The new or already existing StatsStructType. + */ virtual StatsStructType& getOrCreate() PURE; + /** + * Returns whether or not the underlying stats have been initialized yet. + * + * @return If the underlying stats have been initialized. + */ + virtual bool isPresent() const PURE; + virtual ~DeferredCreationCompatibleInterface() = default; }; @@ -250,6 +261,13 @@ template class DeferredCreationCompatibleStats { inline StatsStructType* operator->() { return &data_->getOrCreate(); }; inline StatsStructType& operator*() { return data_->getOrCreate(); }; + /** + * Returns whether or not the underlying data_ has been initialized yet. + * + * @return If the underlying data_ has been initialized. + */ + bool isPresent() const { return data_->isPresent(); } + private: std::unique_ptr> data_; }; diff --git a/source/common/common/thread.h b/source/common/common/thread.h index f1d2128a2204..5403a29b0cca 100644 --- a/source/common/common/thread.h +++ b/source/common/common/thread.h @@ -139,6 +139,14 @@ class AtomicPtrArray : NonCopyable { return atomic_ref.load(); } + /** + * Returns whether the underlying pointer at the index is null. + * + * @param index the Index to look up. + * @return true if the underlying pointer at the index is null. + */ + bool isNull(uint32_t index) const { return data_[index].load() == nullptr; } + private: std::atomic data_[size]; absl::Mutex mutex_; @@ -166,6 +174,11 @@ class AtomicPtr : private AtomicPtrArray { * @return The new or already-existing T*, possibly nullptr if make_object returns nullptr. */ T* get(const MakeObject& make_object) { return BaseClass::get(0, make_object); } + + /** + * @return true if the underlying pointer is null. + */ + bool isNull() const { return BaseClass::isNull(0); } }; // We use platform-specific functions to determine whether the current thread is diff --git a/source/common/stats/deferred_creation.h b/source/common/stats/deferred_creation.h index 511eb73ac454..9b6946d3297b 100644 --- a/source/common/stats/deferred_creation.h +++ b/source/common/stats/deferred_creation.h @@ -55,6 +55,7 @@ class DeferredStats : public DeferredCreationCompatibleInterface DirectStats(const typename StatsStructType::StatNameType& stat_names, Stats::Scope& scope) : stats_(stat_names, scope) {} inline StatsStructType& getOrCreate() override { return stats_; } + bool isPresent() const override { return true; } private: StatsStructType stats_; diff --git a/test/common/stats/deferred_creation_stats_test.cc b/test/common/stats/deferred_creation_stats_test.cc index f067f0ebbb5a..63fff56fa596 100644 --- a/test/common/stats/deferred_creation_stats_test.cc +++ b/test/common/stats/deferred_creation_stats_test.cc @@ -54,11 +54,15 @@ TEST_F(DeferredCreationStatsTest, StatsGoneWithScope) { MyStats x = createDeferredCompatibleStats(scope, stats_names_, true); MyStats y = createDeferredCompatibleStats(scope, stats_names_, true); EXPECT_EQ(TestUtility::findGauge(store_, "bluh.AwesomeStats.initialized")->value(), 0); + EXPECT_FALSE(x.isPresent()); x->foo_.inc(); EXPECT_EQ(TestUtility::findGauge(store_, "bluh.AwesomeStats.initialized")->value(), 1); + EXPECT_TRUE(x.isPresent()); + EXPECT_FALSE(y.isPresent()); EXPECT_EQ(x->foo_.value(), 1); EXPECT_EQ(y->foo_.value(), 1); EXPECT_EQ(TestUtility::findGauge(store_, "bluh.AwesomeStats.initialized")->value(), 2); + EXPECT_TRUE(y.isPresent()); } // Deleted as scope deleted. EXPECT_EQ(TestUtility::findCounter(store_, "bluh.foo"), nullptr); @@ -68,11 +72,13 @@ TEST_F(DeferredCreationStatsTest, StatsGoneWithScope) { ScopeSharedPtr scope = store_.createScope("bluh"); EXPECT_EQ(TestUtility::findGauge(store_, "bluh.AwesomeStats.initialized"), nullptr); MyStats x = createDeferredCompatibleStats(scope, stats_names_, true); + EXPECT_FALSE(x.isPresent()); EXPECT_EQ(TestUtility::findGauge(store_, "bluh.AwesomeStats.initialized")->value(), 0); // Previous data is gone, as scope_v2 and scope_1's lifecycle do not overlap. EXPECT_EQ(x->foo_.value(), 0); // Initialized now. EXPECT_EQ(TestUtility::findGauge(store_, "bluh.AwesomeStats.initialized")->value(), 1); + EXPECT_TRUE(x.isPresent()); } }