C++11 Threads
Before you dive into secure coding practises, you’ll go over a brief introduction to the modern C++ tools at your disposal.
The goal of developing a secure and robust, well performing application may often require scheduling some of the computational work in a background thread. For example, you might use C++ concurrency for a blockchain app that requires thousands of transactions a second to be as efficient as possible.
A C++ program starts with at least one function on one thread, usually main()
. While C++ does not conceptually have a “main thread”, some platforms such as iOS or QT have a main thread in which UI updates and other event driven actions happen. In many cases you want to offload some other work away from the starting or main thread and into the background so the main thread can continue to provide UI interaction.
Here’s another example. You’re writing software for a bank ATM. The main thread should be responsible for accepting user input and displaying information on the screen. Advertisements for other bank products show up while you’re banking. The program updates the advertisement images daily. In order to update the information, you do not want to interrupt the performance of the main banking functions. They should run smoothly and respond quickly while the less important advertisement updates happen on a separate thread.
std::thread is an easy way to spawn new threads in C++. Depending on the platform, for example, on OS X, the underlying implementation of this class uses pthreads. If you have used pthreads, the terminology is similar for the std::thread
class. Setting up a thread is a simple as:
#include <thread>
void updateInfo(); //function to be called on separate thread
std::thread thread(updateInfo);
Joining Threads
If you want the ability for the initial thread to wait until the spawned thread completes, use the join
function to start the thread. Start the thread with detach
to instruct the thread to run on it’s own, independent of other threads waiting for it’s completion. A detached thread can not be joinable once you set it to detached.
thread.detach();
You can test if a thread is joinable or not:
assert( !thread.joinable() );
Passing in Arguments
To pass in arguments to a thread, append the arguments after the function object:
void someFunction(std::string myString);
std::thread thread(someFunction, std::string(“hello”));
thread.join();
C++ copies arguments that is passes to a thread. If you want to send a reference to the thread, wrap it in a reference object:
void someFunction(std::string const& theString);
std::string someString(“hello”);
std::thread thread(someFunction, std::ref(someString));
Make sure the objects you pass in as a reference are not destroyed before the thread completes. Use the const
keyword when you don’t want the calling function to change the object.
All threads have an identifier of type std::thread::id. You can call get_id
on a particular thread to get it’s id, or you can call std::this_thread::get_id()
to get the id of the current thread you’re in.
Accessing Member Functions
If you would like to start a thread that calls a member function of a class, you’ll need a few more parameters. Say you have a member function, getUpdatedScreenAdvertisments()
, that will call screenAdvertismentNetworkRequest()
on a separate thread:
class BankATMManager
{
private:
void screenAdvertismentNetworkRequest(bool isUserLoggedIn); //called on separate thread
public:
void getUpdatedScreenAdvertisments();
};
To call a function on a class instance, you’ll need to give it the member function and pass it the instance using *this
before you supply any other function arguments:
void BankATMManager::getUpdatedScreenAdvertisments()
{
bool isUserLoggedIn = false;
std::thread thread = std::thread(&BankATMManager::screenAdvertismentNetworkRequest, *this, isUserLoggedIn);
thread.detach();
}
Now you can use the following function correctly:
void BankATMManager::screenAdvertismentNetworkRequest(bool isUserLoggedIn)
{
; //start request...
}
Porting Code
If you’re dealing with portable code and working on iOS or Mac OS X, there are a few other things to keep in mind. If the code mixes Foundation/Cocoa objects, then every thread you spawn should get it’s own autorelease pool:
void BankATMManager::screenAdvertismentNetworkRequest(bool isUserLoggedIn)
{
@autoreleasepool
{
//NSString *string...
}
}
From a detached thread, to perform work back on the main thread you can use Grand Central Dispatch:
void BankATMManager::screenAdvertismentNetworkRequest(bool isUserLoggedIn)
{
//do a bunch of synchronous tasks
//tasks are done
dispatch_async(dispatch_get_main_queue(), ^
{
this->updateUIWithSomeData(someData);
});
}
With this platform, you have pthreads if you are writing in C, NSThread
, NSOperation
and Grand Central Dispatch if working with Cocoa and std::thread
if you’re working in C++. Check out more details about std::thread.
Now that you’ve created concurrent access, you’ll need to secure it. Learn how to do that in the Mutual Exclusion article.