Ready for a Thready!

(Note: CppUTest’s memory leak detector is not currently thread-safe. You will want to turn it off for this exercise only; see http://www.cpputest.org/node/25 for further information. Turning off memory leak detection will cause problems when coding the exercise in Chapter 8, Legacy Challenges.)

We want ThreadPool to handle pulling and executing work on its own. We’ll need a thread. We create a test that shows how we want to send a Work object to the ThreadPool’s add function and let the pool execute the work asynchronously.

c9/6/ThreadPoolTest.cpp
 
TEST(AThreadPool, PullsWorkInAThread) {
 
pool.start();
 
condition_variable wasExecuted;
 
bool​ wasWorked{0};
 
Work work{[&] {
 
unique_lock<mutex> lock(m);
 
wasWorked = true;
 
wasExecuted.notify_all();
 
}};
 
 
pool.add(work);
 
 
unique_lock<mutex> lock(m);
 
CHECK_TRUE(wasExecuted.wait_for(lock, chrono::milliseconds(100),
 
[&] { ​return​ wasWorked; }));
 
}

Our asynchronous approach means that add can return control to the test before the work finishes executing. We need a way to verify that the work actually gets executed. We use a wait/notify scheme. After creating a ThreadPool instance, the test defines a condition variable named wasExecuted. We use this semaphore to prevent the test from completing too quickly.

We create a work item with a function callback that sets a flag and notifies any threads waiting on the wasExecuted condition. Our expectation is that this work item will get executed by the ThreadPool’s worker thread. After the test calls pool.add(work), it creates a mutex lock and then waits until the flag is set. The test fails if the condition variable isn’t cleared in a timely fashion.

We add a start function to our ThreadPool class, deciding that clients must indicate when the ThreadPool should start its worker threads. Since threads don’t start automatically on instantiation of the ThreadPool, the application-specific ThreadPool tests we wrote earlier need not worry about any threading concerns. The start function kicks off a worker thread, which is joined in the destructor if initialized.

The worker function waits for work and then pulls and executes it.

c9/6/ThreadPool.h
 
#include <string>
 
#include <deque>
*
#include <thread>
*
#include <memory>
 
 
#include "Work.h"
 
 
class​ ThreadPool {
 
public​:
*
virtual​ ~ThreadPool() {
*
if​ (workThread_)
*
workThread_->join();
*
}
 
*
void​ start() {
*
workThread_ = std::make_shared<std::thread>(&ThreadPool::worker, this);
*
}
 
// ...
 
private​:
*
void​ worker() {
*
while​ (!hasWork())
*
;
*
pullWork().execute();
*
}
 
 
std::deque<Work> workQueue_;
*
std::shared_ptr<std::thread> workThread_;
 
};

On first execution of the test, we receive an error message.

 
..terminate called after throwing an instance of 'std::system_error'
 
what(): Operation not permitted

A quick web search and a Stackoverflow.com page hit later, we eliminate this error by changing CMakeLists.txt to link in pthread.

c9/6/CMakeLists.txt
 
# ...
 
target_link_libraries(utest CppUTest)
*
target_link_libraries(utest pthread)

Our test runs successfully...sometimes. We need only glance at our deliberately simplistic implementation to know it’s a problem. Were you to create a command-line script that repeatedly runs the tests, you would find that our new threaded test crashes every once in a while with a segmentation fault. We’ll investigate another approach: forcing failure directly from the test itself.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.141.219