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