open-telemetry / open-telemetry/opentelemetry-cpp

thread_local TracerProvider with BatchSpanProcessorFactory causes thread to hang

Open
#3,317 6 comments 0 reactions 1 assignee View on GitHub

@marcalff is already working on this.

Since Mar 24, 2025.

bug triage/needs-information
Dominant language
C++
Stars
1.4k
Forks
632
Avg merge
1d 13h
Merged PRs (30d)
75

Description

Describe your environment
OpenTelemetry-cpp 1.19.0
ISO C++ 14, Legacy MSVC.

Steps to reproduce
create a thread_local unique_ptr to a TracerProvider that uses a BatchSpanProcessor, and have the thread end without explicitly freeing the provider.

MRP:

#include <iostream>
#include <thread>
#include <opentelemetry/exporters/ostream/span_exporter_factory.h>
#include <opentelemetry/sdk/trace/batch_span_processor_factory.h>
#include <opentelemetry/sdk/trace/tracer_provider_factory.h>

namespace trace = opentelemetry::sdk::trace;
thread_local std::unique_ptr<trace::TracerProvider> provider;

void workerThread() {
    auto exporter = opentelemetry::exporter::trace::OStreamSpanExporterFactory::Create(std::cout);
    trace::BatchSpanProcessorOptions processorOptions;
    auto processor = trace::BatchSpanProcessorFactory::Create(std::move(exporter), processorOptions);
    provider = trace::TracerProviderFactory::Create(std::move(processor));
    std::cout << "Provider created\n";
}

int main()
{
    auto thread = std::thread(workerThread);
    thread.join();
    std::cout << "Thread joined\n"; //Execution never reaches this point
}

What is the expected behavior?
The thread should cleanly shutdown the tracer provider and batch span processor on exit.

What is the actual behavior?
The thread appears to deadlock during the implicit BatchSpanProcessor::Shutdown call.

Additional context
Trying to add some telemetry gathering to an application via a dll wrapping opentelemetry-cpp.
The nature of the application means that it uses multiple threads that functionally act as their own processes, so each thread has its own TracerProvider.

It's possible for these threads to end without notice being given to the dll, so we don't get a chance to cleanup the TracerProvider (ie. call provider.reset() in the above code)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.