Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/onnxruntime/core/platform/threadpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ class ThreadPool {

std::string StopProfiling();

const unsigned int creator_pid_;
ThreadOptions thread_options_;

// If a thread pool is created with degree_of_parallelism != 1 then an underlying
Expand Down
12 changes: 10 additions & 2 deletions onnxruntime/core/common/threadpool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ limitations under the License.
#include "core/common/common.h"
#include "core/common/cpuid_info.h"
#include "core/common/eigen_common_wrapper.h"
#include "core/common/logging/logging.h"
#include "core/platform/EigenNonBlockingThreadPool.h"
#include <mutex>
#if !defined(ORT_MINIMAL_BUILD)
Expand Down Expand Up @@ -377,7 +378,7 @@ ThreadPool::ThreadPool(Env* env,
int spin_duration_us,
bool force_hybrid,
unsigned int spin_backoff_max)
: thread_options_(thread_options), force_hybrid_(force_hybrid) {
: creator_pid_(logging::GetProcessId()), thread_options_(thread_options), force_hybrid_(force_hybrid) {
// In the current implementation, a thread pool with degree_of_parallelism==1 uses
// the caller as one of the threads for executing work. Hence we only create
// additional thread(s) for degree_of_parallelism>=2.
Expand Down Expand Up @@ -405,7 +406,14 @@ ThreadPool::ThreadPool(Env* env,
}
}

ThreadPool::~ThreadPool() = default;
ThreadPool::~ThreadPool() {
if (creator_pid_ != logging::GetProcessId()) {
// Intentionally abandon this copied pool. Its destructor would touch inherited
// synchronization state and thread handles owned by the parent. The OS reclaims
// the child's copy when the process exits.
extended_eigen_threadpool_.release();
}
}

// Base case for parallel loops, running iterations 0..total, divided into blocks
// of block_size iterations, and calling into a function that takes a start..end
Expand Down
23 changes: 23 additions & 0 deletions onnxruntime/test/platform/threadpool_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
#include <memory>
#include <functional>

#if defined(__linux__) && !defined(__ANDROID__)
#include <sys/wait.h>
#include <unistd.h>
#endif

#ifdef _WIN32
#include <Windows.h>
#endif
Expand Down Expand Up @@ -233,6 +238,24 @@ void TestStagedMultiLoopSections(const std::string& name, int num_threads, int n
} // namespace

namespace onnxruntime {
#if defined(__linux__) && !defined(__ANDROID__)
TEST(ThreadPoolTest, DestructionAfterForkDoesNotJoinParentThreads) {
auto thread_pool = std::make_unique<ThreadPool>(&Env::Default(), ThreadOptions{}, nullptr, 2);

const pid_t child_pid = fork();
ASSERT_NE(child_pid, -1);
if (child_pid == 0) {
thread_pool.reset();
_exit(0);
}

int status = 0;
ASSERT_EQ(waitpid(child_pid, &status, 0), child_pid);
ASSERT_TRUE(WIFEXITED(status));
EXPECT_EQ(WEXITSTATUS(status), 0);
}
#endif

TEST(ThreadPoolTest, TestParallelFor_0_Thread_NoTask) {
TestParallelFor("TestParallelFor_0_Thread_NoTask", 0, 0);
}
Expand Down