@@ -184,6 +184,22 @@ class MeterCreateInstrumentTest : public ::testing::Test
184184 std::shared_ptr<MetricReader> metric_reader_ptr_{new MockMetricReader ()};
185185};
186186
187+ class TestProcessor : public sdk ::metrics::AttributesProcessor
188+ {
189+ public:
190+ explicit TestProcessor () = default;
191+ ~TestProcessor () override = default ;
192+
193+ sdk::metrics::MetricAttributes process (
194+ const opentelemetry::common::KeyValueIterable &attributes) const noexcept override
195+ {
196+ // Just forward attributes
197+ return sdk::metrics::MetricAttributes (attributes);
198+ }
199+
200+ bool isPresent (nostd::string_view key) const noexcept override { return true ; }
201+ };
202+
187203} // namespace
188204
189205TEST (MeterTest, BasicAsyncTests)
@@ -851,3 +867,46 @@ TEST_F(MeterCreateInstrumentTest, ViewCorrectedDuplicateAsyncInstrumentsByDescri
851867 return true ;
852868 });
853869}
870+
871+ TEST (MeterTest, RecordAfterProviderDestructionWithCustomProcessor_NoResetInMain)
872+ {
873+ std::unique_ptr<AttributesProcessor> processor (new TestProcessor ());
874+
875+ // MeterProvider is owned by unique_ptr for explicit control
876+ std::unique_ptr<MeterProvider> provider (new MeterProvider ());
877+
878+ // Register a View with custom processor
879+ std::unique_ptr<View> view (
880+ new View (" my_counter" , " " , " " , AggregationType::kSum , nullptr , std::move (processor)));
881+ std::unique_ptr<InstrumentSelector> instr_selector (
882+ new InstrumentSelector (InstrumentType::kCounter , " my_counter" , " " ));
883+ std::unique_ptr<MeterSelector> meter_selector (new MeterSelector (" test_meter" , " " , " " ));
884+ provider->AddView (std::move (instr_selector), std::move (meter_selector), std::move (view));
885+
886+ auto meter = provider->GetMeter (" test_meter" );
887+ auto counter = meter->CreateUInt64Counter (" my_counter" );
888+
889+ // Move the counter to the thread
890+ std::atomic<bool > thread_ready{false };
891+ std::atomic<bool > thread_done{false };
892+
893+ std::thread t ([c = std::move (counter), &thread_ready, &thread_done]() mutable {
894+ thread_ready = true ;
895+ std::this_thread::sleep_for (std::chrono::milliseconds (50 ));
896+ // Safe after provider destruction
897+ c->Add (12345 , {{" thread" , " after_provider_destruction" }});
898+ thread_done = true ;
899+ });
900+
901+ // Wait for thread to be ready
902+ while (!thread_ready.load ())
903+ std::this_thread::yield ();
904+
905+ // Destroy the provider (and its storage etc)
906+ provider.reset ();
907+
908+ // Wait for thread to finish
909+ while (!thread_done.load ())
910+ std::this_thread::yield ();
911+ t.join ();
912+ }
0 commit comments