Skip to content

Commit 30322cf

Browse files
[BUG] Prevent lost condition-variable wakeups during OTLP file, periodic metric, and batch span processor shutdown (#4365)
1 parent a30be41 commit 30322cf

3 files changed

Lines changed: 39 additions & 8 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@ Increment the:
290290
for process entity
291291
[#4437](https://github.com/open-telemetry/opentelemetry-cpp/pull/4437)
292292

293+
* [BUG] Prevent lost condition-variable wakeups in OTLP file exporter and periodic
294+
metric exporter
295+
[#4365](https://github.com/open-telemetry/opentelemetry-cpp/pull/4365)
296+
293297
Important changes:
294298

295299
* [API] Never set a null global provider or propagator

‎exporters/otlp/src/otlp_file_client.cc‎

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,10 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender
994994
{
995995
if (file_)
996996
{
997+
{
998+
std::lock_guard<std::mutex> waker_guard{file_->background_thread_waker_lock};
999+
file_->is_shutdown.store(true, std::memory_order_release);
1000+
}
9971001
file_->background_thread_waker_cv.notify_all();
9981002
std::unique_ptr<std::thread> background_flush_thread;
9991003
{
@@ -1133,7 +1137,10 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender
11331137

11341138
bool Shutdown(std::chrono::microseconds timeout) noexcept override
11351139
{
1136-
file_->is_shutdown.store(true, std::memory_order_release);
1140+
{
1141+
std::lock_guard<std::mutex> waker_guard{file_->background_thread_waker_lock};
1142+
file_->is_shutdown.store(true, std::memory_order_release);
1143+
}
11371144

11381145
bool result = ForceFlush(timeout);
11391146
return result;
@@ -1482,21 +1489,26 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender
14821489
break;
14831490
}
14841491

1485-
if (concurrency_file->is_shutdown.load(std::memory_order_acquire))
1486-
{
1487-
break;
1488-
}
1489-
14901492
#ifdef ENABLE_THREAD_INSTRUMENTATION_PREVIEW
14911493
if (thread_instrumentation != nullptr)
14921494
{
14931495
thread_instrumentation->BeforeWait();
14941496
}
14951497
#endif /* ENABLE_THREAD_INSTRUMENTATION_PREVIEW */
14961498

1499+
bool is_shutdown = false;
14971500
{
14981501
std::unique_lock<std::mutex> lk(concurrency_file->background_thread_waker_lock);
1499-
concurrency_file->background_thread_waker_cv.wait_for(lk, flush_interval);
1502+
// Even though is_shutdown is atomic, the lock guarantees that either a change to
1503+
// is_shutdown will be observed, or background_thread_waker_cv will see the notification
1504+
// at shutdown. It is important to set is_shutdown prior to `wait_for` rather than
1505+
// as part of a condition in `wait_for` so that a shutdown while the thread is in
1506+
// `wait_for` will still call `std::fflush` below.
1507+
is_shutdown = concurrency_file->is_shutdown.load(std::memory_order_acquire);
1508+
if (!is_shutdown)
1509+
{
1510+
concurrency_file->background_thread_waker_cv.wait_for(lk, flush_interval);
1511+
}
15001512
}
15011513

15021514
#ifdef ENABLE_THREAD_INSTRUMENTATION_PREVIEW
@@ -1506,6 +1518,11 @@ class OPENTELEMETRY_LOCAL_SYMBOL OtlpFileSystemBackend : public OtlpFileAppender
15061518
}
15071519
#endif /* ENABLE_THREAD_INSTRUMENTATION_PREVIEW */
15081520

1521+
if (is_shutdown)
1522+
{
1523+
break;
1524+
}
1525+
15091526
{
15101527
std::size_t current_record_count =
15111528
concurrency_file->record_count.load(std::memory_order_acquire);

‎sdk/src/metrics/export/periodic_exporting_metric_reader.cc‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,12 @@ bool PeriodicExportingMetricReader::OnForceFlush(std::chrono::microseconds timeo
221221
if (force_flush_pending_sequence_.load(std::memory_order_acquire) >
222222
force_flush_notified_sequence_.load(std::memory_order_acquire))
223223
{
224-
is_force_wakeup_background_worker_.store(true, std::memory_order_release);
224+
{
225+
// Acquiring cv_m_ guarantees that the worker thread either is not currently waiting on cv_,
226+
// or the notify below will cause it to re-check the wait condition.
227+
std::lock_guard<std::mutex> cv_guard{cv_m_};
228+
is_force_wakeup_background_worker_.store(true, std::memory_order_release);
229+
}
225230
cv_.notify_all();
226231
}
227232
return force_flush_notified_sequence_.load(std::memory_order_acquire) >= current_sequence;
@@ -283,6 +288,11 @@ bool PeriodicExportingMetricReader::OnShutDown(std::chrono::microseconds timeout
283288
{
284289
if (worker_thread_.joinable())
285290
{
291+
{
292+
// Acquiring cv_m_ guarantees that the next time the worker thread checks the wait condition
293+
// on cv_ (either from notify below or any other reason) it will see IsShutdown() return true.
294+
std::lock_guard<std::mutex> cv_guard{cv_m_};
295+
}
286296
cv_.notify_all();
287297
worker_thread_.join();
288298
}

0 commit comments

Comments
 (0)