|
6 | 6 | #include <stdint.h> |
7 | 7 | #include <algorithm> |
8 | 8 | #include <atomic> |
| 9 | +#include <chrono> |
9 | 10 | #include <iostream> |
10 | 11 | #include <string> |
11 | 12 | #include <thread> |
| 13 | +#include <type_traits> |
12 | 14 | #include <utility> |
13 | 15 | #include <vector> |
14 | 16 | #include "common.h" |
15 | 17 |
|
16 | 18 | #include <functional> |
| 19 | +#include "opentelemetry/common/key_value_iterable.h" |
17 | 20 | #include "opentelemetry/context/context.h" |
18 | 21 | #include "opentelemetry/metrics/async_instruments.h" |
19 | 22 | #include "opentelemetry/metrics/meter.h" |
20 | 23 | #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" |
21 | 24 | #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" |
22 | 25 | #include "opentelemetry/sdk/metrics/instruments.h" |
23 | 26 | #include "opentelemetry/sdk/metrics/meter_config.h" |
| 27 | +#include "opentelemetry/sdk/metrics/view/attributes_processor.h" |
24 | 28 | #include "opentelemetry/sdk/metrics/view/view_registry.h" |
25 | 29 | #include "opentelemetry/sdk/resource/resource.h" |
26 | 30 |
|
|
31 | 35 | #include "opentelemetry/metrics/sync_instruments.h" // IWYU pragma: keep |
32 | 36 | #include "opentelemetry/nostd/function_ref.h" |
33 | 37 | #include "opentelemetry/nostd/shared_ptr.h" |
| 38 | +#include "opentelemetry/nostd/string_view.h" |
34 | 39 | #include "opentelemetry/nostd/variant.h" |
35 | 40 | #include "opentelemetry/sdk/common/attribute_utils.h" |
36 | 41 | #include "opentelemetry/sdk/common/global_log_handler.h" |
@@ -184,6 +189,22 @@ class MeterCreateInstrumentTest : public ::testing::Test |
184 | 189 | std::shared_ptr<MetricReader> metric_reader_ptr_{new MockMetricReader()}; |
185 | 190 | }; |
186 | 191 |
|
| 192 | +class TestProcessor : public sdk::metrics::AttributesProcessor |
| 193 | +{ |
| 194 | +public: |
| 195 | + explicit TestProcessor() = default; |
| 196 | + ~TestProcessor() override = default; |
| 197 | + |
| 198 | + sdk::metrics::MetricAttributes process( |
| 199 | + const opentelemetry::common::KeyValueIterable &attributes) const noexcept override |
| 200 | + { |
| 201 | + // Just forward attributes |
| 202 | + return sdk::metrics::MetricAttributes(attributes); |
| 203 | + } |
| 204 | + |
| 205 | + bool isPresent(nostd::string_view /*key*/) const noexcept override { return true; } |
| 206 | +}; |
| 207 | + |
187 | 208 | } // namespace |
188 | 209 |
|
189 | 210 | TEST(MeterTest, BasicAsyncTests) |
@@ -851,3 +872,46 @@ TEST_F(MeterCreateInstrumentTest, ViewCorrectedDuplicateAsyncInstrumentsByDescri |
851 | 872 | return true; |
852 | 873 | }); |
853 | 874 | } |
| 875 | + |
| 876 | +TEST(MeterTest, RecordAfterProviderDestructionWithCustomProcessor_NoResetInMain) |
| 877 | +{ |
| 878 | + std::unique_ptr<AttributesProcessor> processor(new TestProcessor()); |
| 879 | + |
| 880 | + // MeterProvider is owned by unique_ptr for explicit control |
| 881 | + std::unique_ptr<MeterProvider> provider(new MeterProvider()); |
| 882 | + |
| 883 | + // Register a View with custom processor |
| 884 | + std::unique_ptr<View> view( |
| 885 | + new View("my_counter", "", "", AggregationType::kSum, nullptr, std::move(processor))); |
| 886 | + std::unique_ptr<InstrumentSelector> instr_selector( |
| 887 | + new InstrumentSelector(InstrumentType::kCounter, "my_counter", "")); |
| 888 | + std::unique_ptr<MeterSelector> meter_selector(new MeterSelector("test_meter", "", "")); |
| 889 | + provider->AddView(std::move(instr_selector), std::move(meter_selector), std::move(view)); |
| 890 | + |
| 891 | + auto meter = provider->GetMeter("test_meter"); |
| 892 | + auto counter = meter->CreateUInt64Counter("my_counter"); |
| 893 | + |
| 894 | + // Move the counter to the thread |
| 895 | + std::atomic<bool> thread_ready{false}; |
| 896 | + std::atomic<bool> thread_done{false}; |
| 897 | + |
| 898 | + std::thread t([c = std::move(counter), &thread_ready, &thread_done]() mutable { |
| 899 | + thread_ready = true; |
| 900 | + std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| 901 | + // Safe after provider destruction |
| 902 | + c->Add(12345, {{"thread", "after_provider_destruction"}}); |
| 903 | + thread_done = true; |
| 904 | + }); |
| 905 | + |
| 906 | + // Wait for thread to be ready |
| 907 | + while (!thread_ready.load()) |
| 908 | + std::this_thread::yield(); |
| 909 | + |
| 910 | + // Destroy the provider (and its storage etc) |
| 911 | + provider.reset(); |
| 912 | + |
| 913 | + // Wait for thread to finish |
| 914 | + while (!thread_done.load()) |
| 915 | + std::this_thread::yield(); |
| 916 | + t.join(); |
| 917 | +} |
0 commit comments