opentelemetry-cpp icon indicating copy to clipboard operation
opentelemetry-cpp copied to clipboard

Fix MeterProvider destructor warning when Shutdown() called manually

Open Copilot opened this issue 5 months ago • 8 comments

Problem

When MeterProvider::Shutdown() is called manually, the destructor still calls Shutdown() again, which causes MeterContext to emit a warning:

[MeterContext::Shutdown] Shutdown can be invoked only once.

This creates unnecessary noise in logs when users properly shut down their meter providers manually before destruction.

Root Cause

The MeterProvider destructor unconditionally calls context_->Shutdown() without checking if shutdown has already been performed:

MeterProvider::~MeterProvider()
{
  if (context_)
  {
    context_->Shutdown();  // Always calls shutdown
  }
}

Solution

This PR implements the same pattern used by other components in the codebase like BatchSpanProcessor and SimpleLogRecordProcessor:

  1. Added IsShutdown() method to MeterContext - Allows checking shutdown state without side effects
  2. Added atomic<bool> is_shutdown_ member - Tracks shutdown state independently of the shutdown latch
  3. Updated MeterProvider destructor - Only calls Shutdown() if not already shut down
  4. Preserved existing behavior - Manual duplicate shutdown calls still emit warnings as expected

Changes

Before:

MeterProvider::~MeterProvider()
{
  if (context_)
  {
    context_->Shutdown();  // Always warns if shutdown was called manually
  }
}

After:

MeterProvider::~MeterProvider()
{
  if (context_ && !context_->IsShutdown())
  {
    context_->Shutdown();  // Only shutdown if not already done
  }
}

Testing

  • ✅ Existing tests pass without destructor warnings
  • ✅ New ShutdownTwice test validates the fix
  • ✅ Manual duplicate shutdown calls still warn appropriately
  • ✅ All metrics tests continue to pass

Before Fix

[Warning] File: .../meter_context.cc:178 [MeterContext::Shutdown] Shutdown can be invoked only once.

After Fix

No warnings from destructor when Shutdown() was called manually.

Fixes #3511.

[!WARNING]

Firewall rules blocked me from connecting to one or more addresses

I tried to connect to the following addresses, but was blocked by firewall rules:

  • esm.ubuntu.com
    • Triggering command: /usr/lib/apt/methods/https (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot avatar Jul 02 '25 01:07 Copilot

CLA Signed

The committers listed above are authorized under a signed CLA.

  • :white_check_mark: login: Copilot / name: copilot-swe-agent[bot] (7b1872b45e5d329fe3bbcbae0aa41d84758e8c51, 1d9a4de137392e8054812a374978dfcff14a7dd8, aff62f54400152937d583c1f2257251631c0f74b)
  • :white_check_mark: login: lalitb / name: Lalit Kumar Bhasin (4de0bd5bc742c27a714b2c4ce18a306f51743b61)
  • :white_check_mark: login: lalitb / name: lalitb (7b1872b45e5d329fe3bbcbae0aa41d84758e8c51)
  • :white_check_mark: login: ThomsonTan / name: Tom Tan (aff62f54400152937d583c1f2257251631c0f74b)

Deploy Preview for opentelemetry-cpp-api-docs canceled.

Name Link
Latest commit 4de0bd5bc742c27a714b2c4ce18a306f51743b61
Latest deploy log https://app.netlify.com/projects/opentelemetry-cpp-api-docs/deploys/68753e6e89d1040008f8b1d8

netlify[bot] avatar Jul 02 '25 01:07 netlify[bot]

Codecov Report

:white_check_mark: All modified and coverable lines are covered by tests. :white_check_mark: Project coverage is 89.97%. Comparing base (62ba4de) to head (4de0bd5). :warning: Report is 44 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3513      +/-   ##
==========================================
- Coverage   90.01%   89.97%   -0.03%     
==========================================
  Files         219      219              
  Lines        7044     7047       +3     
==========================================
  Hits         6340     6340              
- Misses        704      707       +3     
Files with missing lines Coverage Δ
sdk/src/metrics/meter_context.cc 78.21% <100.00%> (+0.88%) :arrow_up:
sdk/src/metrics/meter_provider.cc 87.18% <100.00%> (ø)

... and 3 files with indirect coverage changes

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov[bot] avatar Jul 02 '25 07:07 codecov[bot]

Please fix the below format error from CI.

The following files have changes:
sdk/test/metrics/meter_provider_sdk_test.cc
diff --git a/sdk/test/metrics/meter_provider_sdk_test.cc b/sdk/test/metrics/meter_provider_sdk_test.cc
index 2254be0..bf05572 100644
--- a/sdk/test/metrics/meter_provider_sdk_test.cc
+++ b/sdk/test/metrics/meter_provider_sdk_test.cc
@@ -328,12 +328,12 @@ TEST(MeterProvider, ShutdownTwice)
 {
   // Test that calling Shutdown twice doesn't emit warnings
   MeterProvider mp;
-  
+
   // First shutdown should succeed
   EXPECT_TRUE(mp.Shutdown());
-  
+
   // Second shutdown should also succeed without warnings
   EXPECT_TRUE(mp.Shutdown());
-  
+
   // Destructor should not emit warnings either since we already shut down
 }
Error: Process completed with exit code 1.

ThomsonTan avatar Jul 02 '25 23:07 ThomsonTan

Please fix the below format error from CI.

...

Fixed the formatting error by removing trailing whitespace from the test file. Commit aff62f5

Copilot avatar Jul 02 '25 23:07 Copilot

See related:

  • https://github.com/open-telemetry/community/issues/2851

marcalff avatar Jul 03 '25 19:07 marcalff

/easycla

marcalff avatar Aug 29 '25 19:08 marcalff

I guess the problem is located deeper in the design of Shutdown(). It should be clarified whether it should be possible to add new collectors after Shutdown(), which is currently possible. This opens the next design flaw, GetCollectors() is public API, which would allow access to already shut down collectors. Additionally, MeterContext is derived from std::enable_shared_from_this<>, which implies a possible shared state. But then everyone holding this shared pointer is allowed to call ShutDown().

Reneg973 avatar Oct 25 '25 02:10 Reneg973