mutable mutexes

const member functions

If a member function does not change the object it belongs to, it should be marked as const. This does not only help the reader to easier understand the code, but also the compiler to optimize the binary. A const member function can be called on a const object. So far, so good. But what if you need to make such a method thread safe?

logical constness

Logical constness describes when it is Ok to make an object member mutable, and is thoroughly explained in the book Effective C++ by Scott Meyers. Contrary to bitwise constness, logical constness allows to modify some of the members of the object: the ones that are not important or meaningful to the logic of the class. Such members could be technical implementation details as e.g. mutexes, and we should be fine using the mutable keyword here:

class Foo {

    void doSomething() const {
        std::lock_guard<std::mutex> lock;
        readFromStructure();
    };

private:
    mutable std::mutex mutex_;
};

bitwise constness

Bitwise const is when a method doesn’t and isn’t allowed to modify any of the non-static members of the object at all. This is C++’s default behavior, and one should consider carefully which member can be made mutable.

std::atomic default value

std::atomic is a template that allows you to declare its contained value to be atomic: access to this value from different threads is well defined and does not lead to data races.

How to set values

std::atomic is not copyable and not movable.

Lets say we need an atomic value as a class member and want it to be set to a default value:

#include <atomic>

class Foo {
    std::atomic<bool> _online = true; // intuitive, but doesn't work
}

That doesn’t compile. Clang will tell us: "copying member subobject of type 'std::atomic_bool' (aka 'atomic<bool>') invokes deleted constructor."

Use the initialization constructor

Default values to atomic members can be assigned using an initializer list {} (braced initializer, or C++11 brace-init):

std::atomic<bool> _online{true};

For the complete API, refer to https://www.cplusplus.com/reference/atomic/atomic/

Aliases

And for those who want to improve readability a bit more, there are aliases we can use:

std::atomic_bool _online{true};

std::atomic_short
std::atomic_ushort
std::atomic_int
std::atomic_uint
std::atomic_long
std::atomic_ulong
std::atomic_llong
std::atomic_ullong
std::atomic_char8_t
std::atomic_char16_t
std::atomic_char32_t
std::atomic_wchar_t
std::atomic_int8_t
std::atomic_uint8_t
std::atomic_int16_t
std::atomic_uint16_t
std::atomic_int32_t
std::atomic_uint32_t
std::atomic_int64_t
std::atomic_uint64_t
std::atomic_int_least8_t
std::atomic_uint_least8_t
std::atomic_int_least16_t
std::atomic_uint_least16_t
std::atomic_int_least32_t
std::atomic_uint_least32_t
std::atomic_int_least64_t
std::atomic_uint_least64_t
std::atomic_int_fast8_t
std::atomic_uint_fast8_t
std::atomic_int_fast16_t
std::atomic_uint_fast16_t
std::atomic_int_fast32_t
std::atomic_uint_fast32_t
std::atomic_int_fast64_t
std::atomic_uint_fast64_t
std::atomic_intptr_t
std::atomic_uintptr_t
std::atomic_size_t
std::atomic_ptrdiff_t
std::atomic_intmax_t
std::atomic_uintmax_t

Tools

To detect data races you can use ThreadSanitizer, for example when you run your automated tests. It will tell you where to optimize your code.

clang++ -fsanitize=thread

And in cmake:

cmake -DCMAKE_C_FLAGS="-fsanitize=thread"

More about tsan here: https://clang.llvm.org/docs/ThreadSanitizer.html

async unit tests in C++

Sometimes we are in the situation where we need to test the result of a callback that is invoked on another thread and we don’t know when this will happen.

The problem? Our test already runs out of scope before the callback gets called. In order to test such a scenario we’ll need the following:

Lets assume we have a downloader that takes a while to download something, and (hopefully) reports true when it was successful:

class Downloader {
    asyncDownload(std::string url, std::function<void(bool)> callback) {
        std::thread downloadThread(download, std::move(callback));
        downloadThread.join();
    }

    download(std::string url, std::function<void(bool)> callback) {

        // perform download which takes a while
        ...
        if (everythingWentWell)
            callback(true);
    }
};

We could start writing our test case like this:

TEST(Downloader, testDownload) {
    auto callback = [](bool success) {
        EXPECT_TRUE(success);
    };
    Downloader downloader;
    downloader.download("http://foo.bar/interesting-file.jpg", std::move(callback));
}

This will leave us in a situation where the test case finishes while the Downloader is still downloading. What we need now is a way for the test case to wait until the callback is invoked, with a maximum timeout.

The way to go here is a promise with a 30s max timeout:

std::promise<void> waitGuard;
...
    EXPECT_EQ(boost::future_status::ready, waitGuard.get_future().wait_for(std::chrono::seconds(30)));

When the callback gets invoked, we inform the wait guard by setting a value. The test case now looks like this:

TEST(Downloader, testDownload) {
    std::promise<void> waitGuard;
    auto callback = [&](bool success) {
        // remember this gets called on the other thread
        EXPECT_TRUE(success);
        waitGuard.set_value();
    };
    Downloader downloader;
    downloader.download("http://foo.bar/interesting-file.jpg", std::move(callback));
    EXPECT_EQ(boost::future_status::ready, waitGuard.get_future().wait_for(std::chrono::seconds(30)));
}

Now, the test will wait until the download finishes and the callback gets called, but not more than 30s.