1 // Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8
9 #include <gtest/gtest.h>
10
11 #include <exceptions/exceptions.h>
12 #include <util/thread_pool.h>
13
14 #include <signal.h>
15
16 using namespace isc;
17 using namespace isc::util;
18 using namespace std;
19
20 namespace {
21
22 /// @brief define CallBack type
23 typedef function<void()> CallBack;
24
25 /// @brief Test Fixture for testing isc::dhcp::ThreadPool
26 class ThreadPoolTest : public ::testing::Test {
27 public:
28 /// @brief Constructor
ThreadPoolTest()29 ThreadPoolTest() : thread_count_(0), count_(0), wait_(false) {
30 }
31
32 /// @brief task function which registers the thread id and signals main
33 /// thread to stop waiting and then waits for main thread to signal to exit
runAndWait()34 void runAndWait() {
35 // run task
36 run();
37 // wait for main thread signal to exit
38 unique_lock<mutex> lk(wait_mutex_);
39 wait_cv_.wait(lk, [&]() {return (wait() == false);});
40 }
41
42 /// @brief task function which registers the thread id and signals main
43 /// thread to stop waiting
run()44 void run() {
45 {
46 // make sure this thread has started and it is accounted for
47 lock_guard<mutex> lk(mutex_);
48 auto id = this_thread::get_id();
49 // register this thread as doing work on items
50 ids_.emplace(id);
51 // finish task
52 ++count_;
53 // register this task on the history of this thread
54 history_[id].push_back(count_);
55 }
56 // wake main thread if it is waiting for this thread to process
57 cv_.notify_all();
58 }
59
60 /// @brief task function which tries to stop the thread pool and then calls
61 /// @ref runAndWait
runStopResetAndWait(ThreadPool<CallBack> * thread_pool)62 void runStopResetAndWait(ThreadPool<CallBack>* thread_pool) {
63 EXPECT_THROW(thread_pool->stop(), MultiThreadingInvalidOperation);
64 EXPECT_THROW(thread_pool->reset(), MultiThreadingInvalidOperation);
65 EXPECT_THROW(thread_pool->wait(), MultiThreadingInvalidOperation);
66 EXPECT_THROW(thread_pool->wait(0), MultiThreadingInvalidOperation);
67 sigset_t nsset;
68 pthread_sigmask(SIG_SETMASK, 0, &nsset);
69 EXPECT_EQ(1, sigismember(&nsset, SIGCHLD));
70 EXPECT_EQ(1, sigismember(&nsset, SIGINT));
71 EXPECT_EQ(1, sigismember(&nsset, SIGHUP));
72 EXPECT_EQ(1, sigismember(&nsset, SIGTERM));
73 EXPECT_NO_THROW(runAndWait());
74 }
75
76 /// @brief reset all counters and internal test state
reset(uint32_t thread_count)77 void reset(uint32_t thread_count) {
78 // stop test threads
79 stopThreads();
80 // reset the internal state of the test
81 thread_count_ = thread_count;
82 count_ = 0;
83 wait_ = true;
84 ids_.clear();
85 history_.clear();
86 }
87
88 /// @brief start test threads and block main thread until all tasks have
89 /// been processed
90 ///
91 /// @param thread_count number of threads to be started
92 /// @param items_count number of items to wait for being processed
93 /// @param start create processing threads
94 /// @param signal give main thread control over the threads exit
waitTasks(uint32_t thread_count,uint32_t items_count,bool start=false,bool signal=true)95 void waitTasks(uint32_t thread_count, uint32_t items_count,
96 bool start = false, bool signal = true) {
97 // make sure we have all threads running when performing all the checks
98 unique_lock<mutex> lck(mutex_);
99 if (start) {
100 // start test threads if explicitly specified
101 startThreads(thread_count, signal);
102 }
103 // wait for the threads to process all the items
104 cv_.wait(lck, [&]() {return (count() == items_count);});
105 }
106
107 /// @brief start test threads
108 ///
109 /// @param thread_count number of threads to be started
110 /// @param signal give main thread control over the threads exit
startThreads(uint32_t thread_count,bool signal=true)111 void startThreads(uint32_t thread_count, bool signal = true) {
112 // set default task function to wait for main thread signal
113 auto runFunction = &ThreadPoolTest::runAndWait;
114 if (!signal) {
115 // set the task function to finish immediately
116 runFunction = &ThreadPoolTest::run;
117 }
118 // start test threads
119 for (uint32_t i = 0; i < thread_count; ++i) {
120 threads_.push_back(boost::make_shared<std::thread>(runFunction, this));
121 }
122 }
123
124 /// @brief stop test threads
stopThreads()125 void stopThreads() {
126 // signal threads that are waiting
127 signalThreads();
128 // wait for all test threads to exit
129 for (auto thread : threads_) {
130 thread->join();
131 }
132 // reset all threads
133 threads_.clear();
134 }
135
136 /// @brief function used by main thread to unblock processing threads
signalThreads()137 void signalThreads() {
138 {
139 lock_guard<mutex> lk(wait_mutex_);
140 // clear the wait flag so that threads will no longer wait for the main
141 // thread signal
142 wait_ = false;
143 }
144 // wake all threads if waiting for main thread signal
145 wait_cv_.notify_all();
146 }
147
148 /// @brief number of completed tasks
149 ///
150 /// @return the number of completed tasks
count()151 uint32_t count() {
152 return (count_);
153 }
154
155 /// @brief flag which indicates if working thread should wait for main
156 /// thread signal
157 ///
158 /// @return the wait flag
wait()159 bool wait() {
160 return (wait_);
161 }
162
163 /// @brief check the total number of tasks that have been processed
164 /// Some of the tasks may have been run on the same thread and none may have
165 /// been processed by other threads
checkRunHistory(uint32_t items_count)166 void checkRunHistory(uint32_t items_count) {
167 uint32_t count = 0;
168 // iterate over all threads history and count all the processed tasks
169 for (auto element : history_) {
170 count += element.second.size();
171 }
172 ASSERT_EQ(count, items_count);
173 }
174
175 /// @brief check the total number of threads that have processed tasks
checkIds(uint32_t count)176 void checkIds(uint32_t count) {
177 ASSERT_EQ(ids_.size(), count);
178 }
179
180 private:
181 /// @brief thread count used by the test
182 uint32_t thread_count_;
183
184 /// @brief mutex used to keep the internal state consistent
185 std::mutex mutex_;
186
187 /// @brief condition variable used to signal main thread that all threads
188 /// have started processing
189 condition_variable cv_;
190
191 /// @brief mutex used to keep the internal state consistent
192 /// related to the control of the main thread over the working threads exit
193 std::mutex wait_mutex_;
194
195 /// @brief condition variable used to signal working threads to exit
196 condition_variable wait_cv_;
197
198 /// @brief number of completed tasks
199 uint32_t count_;
200
201 /// @brief flag which indicates if working thread should wait for main
202 /// thread signal
203 bool wait_;
204
205 /// @brief the set of thread ids which have completed tasks
206 set<std::thread::id> ids_;
207
208 /// @brief the list of completed tasks run by each thread
209 map<std::thread::id, list<uint32_t>> history_;
210
211 /// @brief the list of test threads
212 list<boost::shared_ptr<std::thread>> threads_;
213 };
214
215 /// @brief test ThreadPool add and count
TEST_F(ThreadPoolTest,addAndCount)216 TEST_F(ThreadPoolTest, addAndCount) {
217 uint32_t items_count;
218 CallBack call_back;
219 ThreadPool<CallBack> thread_pool;
220 // the item count should be 0
221 ASSERT_EQ(thread_pool.count(), 0);
222 // the thread count should be 0
223 ASSERT_EQ(thread_pool.size(), 0);
224
225 items_count = 4;
226
227 call_back = std::bind(&ThreadPoolTest::run, this);
228
229 // add items to stopped thread pool
230 for (uint32_t i = 0; i < items_count; ++i) {
231 bool ret = true;
232 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
233 EXPECT_TRUE(ret);
234 }
235
236 // the item count should match
237 ASSERT_EQ(thread_pool.count(), items_count);
238
239 // calling reset should clear all threads and should remove all queued items
240 EXPECT_NO_THROW(thread_pool.reset());
241 // the item count should be 0
242 ASSERT_EQ(thread_pool.count(), 0);
243 // the thread count should be 0
244 ASSERT_EQ(thread_pool.size(), 0);
245 }
246
247 /// @brief test ThreadPool start and stop
TEST_F(ThreadPoolTest,startAndStop)248 TEST_F(ThreadPoolTest, startAndStop) {
249 uint32_t items_count;
250 uint32_t thread_count;
251 CallBack call_back;
252 ThreadPool<CallBack> thread_pool;
253 // the item count should be 0
254 ASSERT_EQ(thread_pool.count(), 0);
255 // the thread count should be 0
256 ASSERT_EQ(thread_pool.size(), 0);
257
258 items_count = 4;
259 thread_count = 4;
260 // prepare setup
261 reset(thread_count);
262
263 // create tasks which block thread pool threads until signaled by main
264 // thread to force all threads of the thread pool to run exactly one task
265 call_back = std::bind(&ThreadPoolTest::runAndWait, this);
266
267 // calling start should create the threads and should keep the queued items
268 EXPECT_THROW(thread_pool.start(0), InvalidParameter);
269 // calling start should create the threads and should keep the queued items
270 EXPECT_NO_THROW(thread_pool.start(thread_count));
271 // the item count should be 0
272 ASSERT_EQ(thread_pool.count(), 0);
273 // the thread count should match
274 ASSERT_EQ(thread_pool.size(), thread_count);
275
276 // do it once again to check if it works
277 EXPECT_THROW(thread_pool.start(thread_count), InvalidOperation);
278 // the item count should be 0
279 ASSERT_EQ(thread_pool.count(), 0);
280 // the thread count should match
281 ASSERT_EQ(thread_pool.size(), thread_count);
282
283 // calling stop should clear all threads and should keep queued items
284 EXPECT_NO_THROW(thread_pool.stop());
285 // the item count should be 0
286 ASSERT_EQ(thread_pool.count(), 0);
287 // the thread count should be 0
288 ASSERT_EQ(thread_pool.size(), 0);
289
290 // do it once again to check if it works
291 EXPECT_THROW(thread_pool.stop(), InvalidOperation);
292 // the item count should be 0
293 ASSERT_EQ(thread_pool.count(), 0);
294 // the thread count should be 0
295 ASSERT_EQ(thread_pool.size(), 0);
296
297 // add items to stopped thread pool
298 for (uint32_t i = 0; i < items_count; ++i) {
299 bool ret = true;
300 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
301 EXPECT_TRUE(ret);
302 }
303
304 // the item count should match
305 ASSERT_EQ(thread_pool.count(), items_count);
306 // the thread count should be 0
307 ASSERT_EQ(thread_pool.size(), 0);
308
309 // calling stop should clear all threads and should keep queued items
310 EXPECT_THROW(thread_pool.stop(), InvalidOperation);
311 // the item count should match
312 ASSERT_EQ(thread_pool.count(), items_count);
313 // the thread count should be 0
314 ASSERT_EQ(thread_pool.size(), 0);
315
316 // calling reset should clear all threads and should remove all queued items
317 EXPECT_NO_THROW(thread_pool.reset());
318 // the item count should be 0
319 ASSERT_EQ(thread_pool.count(), 0);
320 // the thread count should be 0
321 ASSERT_EQ(thread_pool.size(), 0);
322
323 // do it once again to check if it works
324 EXPECT_NO_THROW(thread_pool.reset());
325 // the item count should be 0
326 ASSERT_EQ(thread_pool.count(), 0);
327 // the thread count should be 0
328 ASSERT_EQ(thread_pool.size(), 0);
329
330 // calling start should create the threads and should keep the queued items
331 EXPECT_NO_THROW(thread_pool.start(thread_count));
332 // the item count should be 0
333 ASSERT_EQ(thread_pool.count(), 0);
334 // the thread count should match
335 ASSERT_EQ(thread_pool.size(), thread_count);
336
337 // add items to running thread pool
338 for (uint32_t i = 0; i < items_count; ++i) {
339 bool ret = true;
340 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
341 EXPECT_TRUE(ret);
342 }
343
344 // wait for all items to be processed
345 waitTasks(thread_count, items_count);
346 // the item count should be 0
347 ASSERT_EQ(thread_pool.count(), 0);
348 // the thread count should match
349 ASSERT_EQ(thread_pool.size(), thread_count);
350 // as each thread pool thread is still waiting on main to unblock, each
351 // thread should have been registered in ids list
352 checkIds(items_count);
353 // all items should have been processed
354 ASSERT_EQ(count(), items_count);
355
356 // check that the number of processed tasks matches the number of items
357 checkRunHistory(items_count);
358
359 // signal thread pool tasks to continue
360 signalThreads();
361
362 // calling stop should clear all threads and should keep queued items
363 EXPECT_NO_THROW(thread_pool.stop());
364 // the item count should be 0
365 ASSERT_EQ(thread_pool.count(), 0);
366 // the thread count should be 0
367 ASSERT_EQ(thread_pool.size(), 0);
368
369 items_count = 64;
370 thread_count = 16;
371 // prepare setup
372 reset(thread_count);
373
374 // create tasks which do not block the thread pool threads so that several
375 // tasks can be run on the same thread and some of the threads never even
376 // having a chance to run
377 call_back = std::bind(&ThreadPoolTest::run, this);
378
379 // add items to stopped thread pool
380 for (uint32_t i = 0; i < items_count; ++i) {
381 bool ret = true;
382 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
383 EXPECT_TRUE(ret);
384 }
385
386 // the item count should match
387 ASSERT_EQ(thread_pool.count(), items_count);
388 // the thread count should be 0
389 ASSERT_EQ(thread_pool.size(), 0);
390
391 // calling start should create the threads and should keep the queued items
392 EXPECT_NO_THROW(thread_pool.start(thread_count));
393 // the thread count should match
394 ASSERT_EQ(thread_pool.size(), thread_count);
395
396 // wait for all items to be processed
397 waitTasks(thread_count, items_count);
398 // the item count should be 0
399 ASSERT_EQ(thread_pool.count(), 0);
400 // the thread count should match
401 ASSERT_EQ(thread_pool.size(), thread_count);
402 // all items should have been processed
403 ASSERT_EQ(count(), items_count);
404
405 // check that the number of processed tasks matches the number of items
406 checkRunHistory(items_count);
407
408 // calling stop should clear all threads and should keep queued items
409 EXPECT_NO_THROW(thread_pool.stop());
410 // the item count should be 0
411 ASSERT_EQ(thread_pool.count(), 0);
412 // the thread count should be 0
413 ASSERT_EQ(thread_pool.size(), 0);
414
415 items_count = 16;
416 thread_count = 16;
417 // prepare setup
418 reset(thread_count);
419
420 // create tasks which try to stop the thread pool and then block thread pool
421 // threads until signaled by main thread to force all threads of the thread
422 // pool to run exactly one task
423 call_back = std::bind(&ThreadPoolTest::runStopResetAndWait, this, &thread_pool);
424
425 // calling start should create the threads and should keep the queued items
426 EXPECT_NO_THROW(thread_pool.start(thread_count));
427 // the item count should be 0
428 ASSERT_EQ(thread_pool.count(), 0);
429 // the thread count should match
430 ASSERT_EQ(thread_pool.size(), thread_count);
431
432 // add items to running thread pool
433 for (uint32_t i = 0; i < items_count; ++i) {
434 bool ret = true;
435 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
436 EXPECT_TRUE(ret);
437 }
438
439 // wait for all items to be processed
440 waitTasks(thread_count, items_count);
441 // the item count should be 0
442 ASSERT_EQ(thread_pool.count(), 0);
443 // the thread count should match
444 ASSERT_EQ(thread_pool.size(), thread_count);
445 // as each thread pool thread is still waiting on main to unblock, each
446 // thread should have been registered in ids list
447 checkIds(items_count);
448 // all items should have been processed
449 ASSERT_EQ(count(), items_count);
450
451 // check that the number of processed tasks matches the number of items
452 checkRunHistory(items_count);
453
454 // signal thread pool tasks to continue
455 signalThreads();
456
457 // calling stop should clear all threads and should keep queued items
458 EXPECT_NO_THROW(thread_pool.stop());
459 // the item count should be 0
460 ASSERT_EQ(thread_pool.count(), 0);
461 // the thread count should be 0
462 ASSERT_EQ(thread_pool.size(), 0);
463
464 /// statistics
465 std::cout << "stat10: " << thread_pool.getQueueStat(10) << std::endl;
466 std::cout << "stat100: " << thread_pool.getQueueStat(100) << std::endl;
467 std::cout << "stat1000: " << thread_pool.getQueueStat(1000) << std::endl;
468 }
469
470 /// @brief test ThreadPool wait
TEST_F(ThreadPoolTest,wait)471 TEST_F(ThreadPoolTest, wait) {
472 uint32_t items_count;
473 uint32_t thread_count;
474 CallBack call_back;
475 ThreadPool<CallBack> thread_pool;
476 // the item count should be 0
477 ASSERT_EQ(thread_pool.count(), 0);
478 // the thread count should be 0
479 ASSERT_EQ(thread_pool.size(), 0);
480
481 items_count = 16;
482 thread_count = 16;
483 // prepare setup
484 reset(thread_count);
485
486 // create tasks which block thread pool threads until signaled by main
487 // thread to force all threads of the thread pool to run exactly one task
488 call_back = std::bind(&ThreadPoolTest::runAndWait, this);
489
490 // add items to stopped thread pool
491 for (uint32_t i = 0; i < items_count; ++i) {
492 bool ret = true;
493 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
494 EXPECT_TRUE(ret);
495 }
496
497 // the item count should match
498 ASSERT_EQ(thread_pool.count(), items_count);
499 // the thread count should be 0
500 ASSERT_EQ(thread_pool.size(), 0);
501
502 // calling start should create the threads and should keep the queued items
503 EXPECT_NO_THROW(thread_pool.start(thread_count));
504 // the thread count should match
505 ASSERT_EQ(thread_pool.size(), thread_count);
506
507 // wait for all items to be processed
508 waitTasks(thread_count, items_count);
509 // the item count should be 0
510 ASSERT_EQ(thread_pool.count(), 0);
511 // the thread count should match
512 ASSERT_EQ(thread_pool.size(), thread_count);
513 // as each thread pool thread is still waiting on main to unblock, each
514 // thread should have been registered in ids list
515 checkIds(items_count);
516 // all items should have been processed
517 ASSERT_EQ(count(), items_count);
518
519 // check that the number of processed tasks matches the number of items
520 checkRunHistory(items_count);
521
522 // check that waiting on tasks does timeout
523 ASSERT_FALSE(thread_pool.wait(1));
524
525 // signal thread pool tasks to continue
526 signalThreads();
527
528 // calling stop should clear all threads and should keep queued items
529 EXPECT_NO_THROW(thread_pool.stop());
530 // the item count should be 0
531 ASSERT_EQ(thread_pool.count(), 0);
532 // the thread count should be 0
533 ASSERT_EQ(thread_pool.size(), 0);
534
535 items_count = 64;
536 thread_count = 16;
537 // prepare setup
538 reset(thread_count);
539
540 // create tasks which do not block the thread pool threads so that several
541 // tasks can be run on the same thread and some of the threads never even
542 // having a chance to run
543 call_back = std::bind(&ThreadPoolTest::run, this);
544
545 // add items to stopped thread pool
546 for (uint32_t i = 0; i < items_count; ++i) {
547 bool ret = true;
548 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
549 EXPECT_TRUE(ret);
550 }
551
552 // the item count should match
553 ASSERT_EQ(thread_pool.count(), items_count);
554 // the thread count should be 0
555 ASSERT_EQ(thread_pool.size(), 0);
556
557 // calling start should create the threads and should keep the queued items
558 EXPECT_NO_THROW(thread_pool.start(thread_count));
559 // the thread count should match
560 ASSERT_EQ(thread_pool.size(), thread_count);
561
562 // wait for all items to be processed
563 thread_pool.wait();
564 // the item count should be 0
565 ASSERT_EQ(thread_pool.count(), 0);
566 // the thread count should match
567 ASSERT_EQ(thread_pool.size(), thread_count);
568 // all items should have been processed
569 ASSERT_EQ(count(), items_count);
570
571 // check that the number of processed tasks matches the number of items
572 checkRunHistory(items_count);
573 }
574
575 /// @brief test ThreadPool max queue size
TEST_F(ThreadPoolTest,maxQueueSize)576 TEST_F(ThreadPoolTest, maxQueueSize) {
577 uint32_t items_count;
578 CallBack call_back;
579 ThreadPool<CallBack> thread_pool;
580 // the item count should be 0
581 ASSERT_EQ(thread_pool.count(), 0);
582 // the thread count should be 0
583 ASSERT_EQ(thread_pool.size(), 0);
584
585 items_count = 20;
586
587 call_back = std::bind(&ThreadPoolTest::run, this);
588
589 // add items to stopped thread pool
590 bool ret = true;
591 for (uint32_t i = 0; i < items_count; ++i) {
592 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
593 EXPECT_TRUE(ret);
594 }
595
596 // the item count should match
597 ASSERT_EQ(thread_pool.count(), items_count);
598
599 // change the max count
600 ASSERT_EQ(thread_pool.getMaxQueueSize(), 0);
601 size_t max_queue_size = 10;
602 thread_pool.setMaxQueueSize(max_queue_size);
603 EXPECT_EQ(thread_pool.getMaxQueueSize(), max_queue_size);
604
605 // adding an item should squeeze the queue
606 EXPECT_EQ(thread_pool.count(), items_count);
607 EXPECT_NO_THROW(ret = thread_pool.add(boost::make_shared<CallBack>(call_back)));
608 EXPECT_FALSE(ret);
609 EXPECT_EQ(thread_pool.count(), max_queue_size);
610 }
611
612 /// @brief test ThreadPool add front.
TEST_F(ThreadPoolTest,addFront)613 TEST_F(ThreadPoolTest, addFront) {
614 uint32_t items_count;
615 CallBack call_back;
616 ThreadPool<CallBack> thread_pool;
617 // the item count should be 0
618 ASSERT_EQ(thread_pool.count(), 0);
619 // the thread count should be 0
620 ASSERT_EQ(thread_pool.size(), 0);
621
622 items_count = 20;
623
624 call_back = std::bind(&ThreadPoolTest::run, this);
625
626 // add items to stopped thread pool
627 bool ret = true;
628 for (uint32_t i = 0; i < items_count; ++i) {
629 EXPECT_NO_THROW(ret = thread_pool.addFront(boost::make_shared<CallBack>(call_back)));
630 EXPECT_TRUE(ret);
631 }
632
633 // the item count should match
634 ASSERT_EQ(thread_pool.count(), items_count);
635
636 // change the max count
637 ASSERT_EQ(thread_pool.getMaxQueueSize(), 0);
638 size_t max_queue_size = 10;
639 thread_pool.setMaxQueueSize(max_queue_size);
640 EXPECT_EQ(thread_pool.getMaxQueueSize(), max_queue_size);
641
642 // adding an item at front should change nothing queue
643 EXPECT_EQ(thread_pool.count(), items_count);
644 EXPECT_NO_THROW(ret = thread_pool.addFront(boost::make_shared<CallBack>(call_back)));
645 EXPECT_FALSE(ret);
646 EXPECT_EQ(thread_pool.count(), items_count);
647 }
648
649 /// @brief test ThreadPool get queue statistics.
TEST_F(ThreadPoolTest,getQueueStat)650 TEST_F(ThreadPoolTest, getQueueStat) {
651 ThreadPool<CallBack> thread_pool;
652 EXPECT_THROW(thread_pool.getQueueStat(0), InvalidParameter);
653 EXPECT_THROW(thread_pool.getQueueStat(1), InvalidParameter);
654 EXPECT_THROW(thread_pool.getQueueStat(-10), InvalidParameter);
655 EXPECT_THROW(thread_pool.getQueueStat(10000), InvalidParameter);
656 EXPECT_NO_THROW(thread_pool.getQueueStat(10));
657 EXPECT_NO_THROW(thread_pool.getQueueStat(100));
658 EXPECT_NO_THROW(thread_pool.getQueueStat(1000));
659 }
660
661 } // namespace
662