Skip to content

fix(thread-pool): use std::call_once for thread-safe diagnostics init#669

Merged
kcenon merged 1 commit into
mainfrom
fix/issue-668-diagnostics-lazy-init-race
Apr 10, 2026
Merged

fix(thread-pool): use std::call_once for thread-safe diagnostics init#669
kcenon merged 1 commit into
mainfrom
fix/issue-668-diagnostics-lazy-init-race

Conversation

@kcenon

@kcenon kcenon commented Apr 10, 2026

Copy link
Copy Markdown
Owner

What

Summary

Replaces the unsynchronized lazy initialization of thread_pool::diagnostics_ with std::call_once to prevent a data race when multiple threads call diagnostics() concurrently for the first time.

Change Type

  • Bugfix (fixes an issue)

Affected Components

  • include/kcenon/thread/core/thread_pool.h — Add std::once_flag member
  • src/impl/thread_pool/thread_pool.cpp — Both diagnostics() overloads

Why

Problem Solved

thread_pool::diagnostics() used a bare if (!diagnostics_) check followed by make_unique without any synchronization. Two threads calling diagnostics() simultaneously for the first time could both observe !diagnostics_ as true and both attempt allocation, causing a data race (undefined behavior). The const overload additionally used const_cast which is UB if the pool was originally declared const.

Related Issues

Where

Files Changed

File Type of Change
include/kcenon/thread/core/thread_pool.h Add mutable std::once_flag diagnostics_init_flag_, #include <mutex>
src/impl/thread_pool/thread_pool.cpp Replace bare if with std::call_once in both overloads
docs/API_REFERENCE.kr.md Fix pre-existing broken TOC anchor

How

Implementation Details

// Before (TOCTOU race)
if (!diagnostics_) {
    diagnostics_ = std::make_unique<...>(*this);
}

// After (thread-safe)
std::call_once(diagnostics_init_flag_, [this]() {
    diagnostics_ = std::make_unique<...>(*this);
});

Breaking Changes

None — diagnostics() return type and behavior unchanged.

thread_pool::diagnostics() used lazy initialization of a mutable
unique_ptr without synchronization. Concurrent first calls from
multiple threads could race on make_unique. Replace with
std::call_once + std::once_flag for guaranteed thread-safe
one-time initialization.

Add mutable std::once_flag diagnostics_init_flag_ member and
#include <mutex> to the header.

Fix pre-existing broken TOC anchor in docs/API_REFERENCE.kr.md
(heading renamed from Lock-Free to Concurrent).

Closes #668
@github-actions

Copy link
Copy Markdown
Contributor

📊 Performance Benchmark Results

Performance Benchmark Report

No benchmark data available.

ℹ️ No baseline reference available

This is the first benchmark run or baseline file is missing.

@kcenon kcenon merged commit 9383f1b into main Apr 10, 2026
27 checks passed
@kcenon kcenon deleted the fix/issue-668-diagnostics-lazy-init-race branch April 10, 2026 05:32
kcenon added a commit that referenced this pull request Apr 13, 2026
…#669)

thread_pool::diagnostics() used lazy initialization of a mutable
unique_ptr without synchronization. Concurrent first calls from
multiple threads could race on make_unique. Replace with
std::call_once + std::once_flag for guaranteed thread-safe
one-time initialization.

Add mutable std::once_flag diagnostics_init_flag_ member and
#include <mutex> to the header.

Fix pre-existing broken TOC anchor in docs/API_REFERENCE.kr.md
(heading renamed from Lock-Free to Concurrent).

Closes #668
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] thread_pool::diagnostics() lazy initialization is not thread-safe

1 participant