From 1e0fba3d896623f3147159f2835e8262ab06cbfc Mon Sep 17 00:00:00 2001 From: vanshaj2023 Date: Mon, 16 Feb 2026 17:50:58 +0530 Subject: [PATCH 1/4] GH-49272: [C++][CI] Fix intermittent segfault in arrow-json-test on MinGW MinGW's __emutls implementation for C++ thread_local has known race conditions during thread creation. When ThreadPool::LaunchWorkersUnlocked creates a new worker thread, the thread writes to the thread_local current_thread_pool_ variable. If __emutls hasn't finished initializing TLS for the new thread, this dereferences an invalid pointer, causing a segfault. Replace thread_local with native Win32 TLS API (TlsAlloc/TlsGetValue/ TlsSetValue) on MinGW to bypass the buggy __emutls emulation. Non-MinGW platforms continue using standard thread_local. Also add a stress test (MultipleChunksParallelStress) that runs parallel JSON reading 20 times to help expose intermittent threading races. --- cpp/src/arrow/json/reader_test.cc | 25 +++++++++++++++++++++ cpp/src/arrow/util/thread_pool.cc | 37 +++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/cpp/src/arrow/json/reader_test.cc b/cpp/src/arrow/json/reader_test.cc index a52626413d6..83d466da268 100644 --- a/cpp/src/arrow/json/reader_test.cc +++ b/cpp/src/arrow/json/reader_test.cc @@ -290,6 +290,31 @@ TEST(ReaderTest, MultipleChunksParallel) { AssertTablesEqual(*serial, *threaded); } +// Regression test for intermittent threading crashes on MinGW. +// Run this test multiple times manually to stress-test: +// while build/debug/arrow-json-test --gtest_filter=ReaderTest.MultipleChunksParallelRegression; do :; done +// See https://github.com/apache/arrow/issues/49272 +TEST(ReaderTest, MultipleChunksParallelRegression) { + int64_t count = 1 << 10; + ParseOptions parse_options; + parse_options.unexpected_field_behavior = UnexpectedFieldBehavior::InferType; + ReadOptions read_options; + read_options.block_size = static_cast(count / 2); + read_options.use_threads = true; + + std::string json; + for (int i = 0; i < count; ++i) { + json += "{\"a\":" + std::to_string(i) + "}\n"; + } + + std::shared_ptr input; + ASSERT_OK(MakeStream(json, &input)); + ASSERT_OK_AND_ASSIGN(auto reader, TableReader::Make(default_memory_pool(), input, + read_options, parse_options)); + ASSERT_OK_AND_ASSIGN(auto table, reader->Read()); + ASSERT_EQ(table->num_rows(), count); +} + TEST(ReaderTest, ListArrayWithFewValues) { // ARROW-7647 ParseOptions parse_options; diff --git a/cpp/src/arrow/util/thread_pool.cc b/cpp/src/arrow/util/thread_pool.cc index bf107006f8b..dc7733542f3 100644 --- a/cpp/src/arrow/util/thread_pool.cc +++ b/cpp/src/arrow/util/thread_pool.cc @@ -26,6 +26,8 @@ #include #include +#include "arrow/util/windows_compatibility.h" + #include "arrow/util/atfork_internal.h" #include "arrow/util/config.h" #include "arrow/util/io_util.h" @@ -630,9 +632,40 @@ void ThreadPool::CollectFinishedWorkersUnlocked() { state_->finished_workers_.clear(); } +// MinGW's __emutls implementation for C++ thread_local has known race conditions +// during thread creation that can cause segfaults. Use native Win32 TLS instead. +// See https://github.com/apache/arrow/issues/49272 +#ifdef __MINGW32__ + +namespace { +DWORD GetPoolTlsIndex() { + static DWORD index = [] { + DWORD i = TlsAlloc(); + if (i == TLS_OUT_OF_INDEXES) { + ARROW_LOG(FATAL) << "TlsAlloc failed for thread pool TLS: " + << WinErrorMessage(GetLastError()); + } + return i; + }(); + return index; +} +} // namespace + +static ThreadPool* GetCurrentThreadPool() { + return static_cast(TlsGetValue(GetPoolTlsIndex())); +} + +static void SetCurrentThreadPool(ThreadPool* pool) { + TlsSetValue(GetPoolTlsIndex(), pool); +} +#else thread_local ThreadPool* current_thread_pool_ = nullptr; -bool ThreadPool::OwnsThisThread() { return current_thread_pool_ == this; } +static ThreadPool* GetCurrentThreadPool() { return current_thread_pool_; } +static void SetCurrentThreadPool(ThreadPool* pool) { current_thread_pool_ = pool; } +#endif + +bool ThreadPool::OwnsThisThread() { return GetCurrentThreadPool() == this; } void ThreadPool::LaunchWorkersUnlocked(int threads) { std::shared_ptr state = sp_state_; @@ -641,7 +674,7 @@ void ThreadPool::LaunchWorkersUnlocked(int threads) { state_->workers_.emplace_back(); auto it = --(state_->workers_.end()); *it = std::thread([this, state, it] { - current_thread_pool_ = this; + SetCurrentThreadPool(this); WorkerLoop(state, it); }); } From 41da5b04f3fd67e33bd1f7a24c29ed4ea3ccf2bd Mon Sep 17 00:00:00 2001 From: vanshaj2023 Date: Fri, 13 Mar 2026 15:59:03 +0530 Subject: [PATCH 2/4] GH-49272: Fix include ordering lint failure in thread_pool.cc --- cpp/src/arrow/util/thread_pool.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/src/arrow/util/thread_pool.cc b/cpp/src/arrow/util/thread_pool.cc index dc7733542f3..7494feb3e51 100644 --- a/cpp/src/arrow/util/thread_pool.cc +++ b/cpp/src/arrow/util/thread_pool.cc @@ -26,13 +26,12 @@ #include #include -#include "arrow/util/windows_compatibility.h" - #include "arrow/util/atfork_internal.h" #include "arrow/util/config.h" #include "arrow/util/io_util.h" #include "arrow/util/logging_internal.h" #include "arrow/util/mutex.h" +#include "arrow/util/windows_compatibility.h" #include "arrow/util/tracing_internal.h" From fbbc431a9bfa41ba2981a3fa4130c9b829a9d900 Mon Sep 17 00:00:00 2001 From: vanshaj2023 Date: Fri, 13 Mar 2026 17:03:44 +0530 Subject: [PATCH 3/4] test --- cpp/src/arrow/util/thread_pool.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/src/arrow/util/thread_pool.cc b/cpp/src/arrow/util/thread_pool.cc index 7494feb3e51..1f13ce4b8b4 100644 --- a/cpp/src/arrow/util/thread_pool.cc +++ b/cpp/src/arrow/util/thread_pool.cc @@ -870,3 +870,5 @@ Status SetCpuThreadPoolCapacity(int threads) { } } // namespace arrow + + From 713faf9909b9c8d6fb77fe637a20f876ef87b158 Mon Sep 17 00:00:00 2001 From: vanshaj2023 Date: Fri, 13 Mar 2026 18:39:20 +0530 Subject: [PATCH 4/4] GH-49272: Fix clang-format lint issues in thread_pool.cc --- cpp/src/arrow/util/thread_pool.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cpp/src/arrow/util/thread_pool.cc b/cpp/src/arrow/util/thread_pool.cc index 1f13ce4b8b4..88c9c3a4d9b 100644 --- a/cpp/src/arrow/util/thread_pool.cc +++ b/cpp/src/arrow/util/thread_pool.cc @@ -634,7 +634,7 @@ void ThreadPool::CollectFinishedWorkersUnlocked() { // MinGW's __emutls implementation for C++ thread_local has known race conditions // during thread creation that can cause segfaults. Use native Win32 TLS instead. // See https://github.com/apache/arrow/issues/49272 -#ifdef __MINGW32__ +# ifdef __MINGW32__ namespace { DWORD GetPoolTlsIndex() { @@ -657,12 +657,12 @@ static ThreadPool* GetCurrentThreadPool() { static void SetCurrentThreadPool(ThreadPool* pool) { TlsSetValue(GetPoolTlsIndex(), pool); } -#else +# else thread_local ThreadPool* current_thread_pool_ = nullptr; static ThreadPool* GetCurrentThreadPool() { return current_thread_pool_; } static void SetCurrentThreadPool(ThreadPool* pool) { current_thread_pool_ = pool; } -#endif +# endif bool ThreadPool::OwnsThisThread() { return GetCurrentThreadPool() == this; } @@ -870,5 +870,3 @@ Status SetCpuThreadPoolCapacity(int threads) { } } // namespace arrow - -