1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <comphelper/threadpool.hxx>
11 #include <cppunit/TestAssert.h>
12 #include <cppunit/TestFixture.h>
13 #include <cppunit/extensions/HelperMacros.h>
14 #include <cppunit/plugin/TestPlugIn.h>
15 #include <tools/time.hxx>
16 #include <osl/thread.hxx>
24 class ThreadPoolTest
: public CppUnit::TestFixture
27 void testPreferredConcurrency();
28 void testWorkerUsage();
29 void testTasksInThreads();
31 void testDedicatedPool();
33 CPPUNIT_TEST_SUITE(ThreadPoolTest
);
34 CPPUNIT_TEST(testPreferredConcurrency
);
35 CPPUNIT_TEST(testWorkerUsage
);
36 CPPUNIT_TEST(testTasksInThreads
);
37 CPPUNIT_TEST(testNoThreads
);
38 CPPUNIT_TEST(testDedicatedPool
);
39 CPPUNIT_TEST_SUITE_END();
42 void ThreadPoolTest::testPreferredConcurrency()
45 auto nThreads
= comphelper::ThreadPool::getPreferredConcurrency();
46 std::size_t nExpected
= 4; // UTs are capped to 4.
47 CPPUNIT_ASSERT_MESSAGE("Expected no more than 4 threads", nExpected
>= nThreads
);
50 // The result should be cached, so this should change anything.
51 nThreads
= std::thread::hardware_concurrency() * 2;
52 setenv("MAX_CONCURRENCY", std::to_string(nThreads
).c_str(), true);
53 nThreads
= comphelper::ThreadPool::getPreferredConcurrency();
54 CPPUNIT_ASSERT_MESSAGE("Expected no more than hardware threads",
55 nThreads
<= std::thread::hardware_concurrency());
57 // Revert and check. Again, nothing should change.
58 unsetenv("MAX_CONCURRENCY");
59 nThreads
= comphelper::ThreadPool::getPreferredConcurrency();
60 CPPUNIT_ASSERT_MESSAGE("Expected no more than 4 threads", nExpected
>= nThreads
);
66 class UsageTask
: public comphelper::ThreadTask
69 UsageTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
)
79 static inline std::atomic
<int> count
= 0;
80 static inline std::mutex mutex
;
84 void ThreadPoolTest::testWorkerUsage()
86 // Create tasks for each available worker. Lock a shared mutex before that to make all
87 // tasks block on it. And check that all workers have started, i.e. that the full
88 // thread pool capacity is used.
89 comphelper::ThreadPool
& rSharedPool
= comphelper::ThreadPool::getSharedOptimalPool();
90 std::shared_ptr
<comphelper::ThreadTaskTag
> pTag
= comphelper::ThreadPool::createThreadTaskTag();
91 UsageTask::mutex
.lock();
92 for (int i
= 0; i
< rSharedPool
.getWorkerCount(); ++i
)
94 rSharedPool
.pushTask(std::make_unique
<UsageTask
>(pTag
));
95 osl::Thread::wait(std::chrono::milliseconds(10)); // give it a time to start
97 sal_uInt64 startTicks
= tools::Time::GetSystemTicks();
98 while (UsageTask::count
!= rSharedPool
.getWorkerCount())
100 // Wait at most 5 seconds, that should do even on slow systems.
101 CPPUNIT_ASSERT_MESSAGE("Thread pool does not use all worker threads.",
102 startTicks
+ 5000 > tools::Time::GetSystemTicks());
103 osl::Thread::wait(std::chrono::milliseconds(10));
105 UsageTask::mutex
.unlock();
106 rSharedPool
.waitUntilDone(pTag
);
111 class CheckThreadTask
: public comphelper::ThreadTask
113 oslThreadIdentifier mThreadId
;
117 CheckThreadTask(oslThreadIdentifier threadId
, bool checkEqual
,
118 const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
)
120 , mThreadId(threadId
)
121 , mCheckEqual(checkEqual
)
124 virtual void doWork()
126 CPPUNIT_ASSERT(mCheckEqual
? osl::Thread::getCurrentIdentifier() == mThreadId
127 : osl::Thread::getCurrentIdentifier() != mThreadId
);
132 void ThreadPoolTest::testTasksInThreads()
134 // Check that all tasks are run in worker threads, not this thread.
135 comphelper::ThreadPool
& pool
= comphelper::ThreadPool::getSharedOptimalPool();
136 std::shared_ptr
<comphelper::ThreadTaskTag
> pTag
= comphelper::ThreadPool::createThreadTaskTag();
137 for (int i
= 0; i
< 8; ++i
)
139 std::make_unique
<CheckThreadTask
>(osl::Thread::getCurrentIdentifier(), false, pTag
));
140 pool
.waitUntilDone(pTag
);
143 void ThreadPoolTest::testNoThreads()
145 // No worker threads, tasks will be run in this thread.
146 comphelper::ThreadPool
pool(0);
147 std::shared_ptr
<comphelper::ThreadTaskTag
> pTag
= comphelper::ThreadPool::createThreadTaskTag();
148 for (int i
= 0; i
< 8; ++i
)
150 std::make_unique
<CheckThreadTask
>(osl::Thread::getCurrentIdentifier(), true, pTag
));
151 pool
.waitUntilDone(pTag
);
154 void ThreadPoolTest::testDedicatedPool()
156 // Test that a separate thread pool works. The tasks themselves do not matter.
157 comphelper::ThreadPool
pool(4);
158 std::shared_ptr
<comphelper::ThreadTaskTag
> pTag
= comphelper::ThreadPool::createThreadTaskTag();
159 for (int i
= 0; i
< 8; ++i
)
161 std::make_unique
<CheckThreadTask
>(osl::Thread::getCurrentIdentifier(), false, pTag
));
162 pool
.waitUntilDone(pTag
);
165 CPPUNIT_TEST_SUITE_REGISTRATION(ThreadPoolTest
);
167 CPPUNIT_PLUGIN_IMPLEMENT();
169 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */