From a23643c22122c23967b3da95ecff1c6bf2ff93be Mon Sep 17 00:00:00 2001 From: Isaac Rowntree Date: Fri, 27 Feb 2026 13:02:05 +1100 Subject: [PATCH] fix: use shutdown() instead of restartPool() in invalidate() to prevent use-after-free Co-Authored-By: Claude Opus 4.6 --- cpp/DBHostObject.cpp | 2 +- cpp/OPThreadPool.cpp | 18 ++++++++++++++++++ cpp/OPThreadPool.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cpp/DBHostObject.cpp b/cpp/DBHostObject.cpp index 96829412..62c6ea3c 100644 --- a/cpp/DBHostObject.cpp +++ b/cpp/DBHostObject.cpp @@ -671,7 +671,7 @@ void DBHostObject::invalidate() { } invalidated = true; - thread_pool->restartPool(); + thread_pool->shutdown(); #ifdef OP_SQLITE_USE_LIBSQL opsqlite_libsql_close(db); #else diff --git a/cpp/OPThreadPool.cpp b/cpp/OPThreadPool.cpp index 93a439bc..eb0d6843 100644 --- a/cpp/OPThreadPool.cpp +++ b/cpp/OPThreadPool.cpp @@ -117,4 +117,22 @@ void ThreadPool::restartPool() { done = false; } +void ThreadPool::shutdown() { + if (done) { + return; + } + + done = true; + + workQueueConditionVariable.notify_all(); + + for (auto &thread : threads) { + if (thread.joinable()) { + thread.join(); + } + } + + threads.clear(); +} + } // namespace opsqlite diff --git a/cpp/OPThreadPool.h b/cpp/OPThreadPool.h index 9405681d..9d9b4d95 100644 --- a/cpp/OPThreadPool.h +++ b/cpp/OPThreadPool.h @@ -17,6 +17,7 @@ class ThreadPool { void queueWork(const std::function &task); void waitFinished(); void restartPool(); + void shutdown(); private: unsigned int busy{};