Articles & Books

Dealing with Mutation: Locking -- Rainer Grimm

dealingwithmutationlocking.pngLocking is a straightforward idea to protect a critical section. A critical section is a section of code that, at most, one thread can use at any time.

Dealing with Mutation: Locking

by Rainer Grimm

From the article:

Scoped locking is the idea of RAII applied to a mutex. Scoped locking is also known as synchronized block and guard. The key idea of this idiom is to bind the resource acquisition and release to an object’s lifetime. As the name suggests, the lifetime of the object is scoped. Scoped means that the C++ run time is responsible for object destruction and, therefore, for releasing the resource.

The class ScopedLock implements Scoped Locking.

The Case of the Two Billion Characters Long String -- Giovanni Dicanio

Several times you need to pass data (including text) from cross-platform C++ code to platform-specific C++ code. If you don't pay enough attention, weird bugs can happen at the boundary.

The Case of the Two Billion Characters Long String

by Giovanni Dicanio

From the article:

What is going on here? What is the origin of this bug? Why is the string reported as being more than 2 billion characters long?

Let's try to solve this mystery, with a sprinkle of assembly language, too!

 

Storage duration and Non-local Objects in C++ -- Bartlomiej Filipek

Filipek-book.pngC++ allows us to declare various forms of non-local objects: they usually live throughout the execution of the whole program. In this article, we’ll look at global variables, dynamic, and thread-local objects. We’ll also consider new features for safe initialization C++20.

Storage duration and Non-local Objects in C++

by Bartlomiej Filipek

From the article:

To start, we need to understand two key properties of an object in C++: storage and linkage. Let’s begin with the definition of storage, from [basic.stc#general]:
"The storage duration is the property of an object that defines the minimum potential lifetime of the storage containing the object. The storage duration is determined by the construct used to create the object."
An object in C++ has one of the following storage duration options:

Bitwise Binary Search: Elegant and Fast -- Orson Peters

peters-bitwisebinarysearch.png

I recently read the article Beautiful Branchless Binary Search by Malte Skarupke. In it they discuss the merits of the following snippet of C++ code implementing a binary search.

Bitwise Binary Search: Elegant and Fast

by Orson Peters

From the article:

In this article I will provide an alternative implementation based on similar ideas but with a very different interpretation that is (in my opinion) incredibly elegant and clear to understand, at least as far as binary searches go. The resulting implementation also saves a comparison in almost every case and ends up quite a bit smaller.

Dealing with Sharing -- Rainer Grimm

temp_file_DealingwithSharing-Grimm1.pngIf you don’t share, no data races can happen. Not sharing means that your thread works on local variables. This can be achieved by copying the value, using thread-specific storage, or transferring the result of a thread to its associated future via a protected data channel.

Dealing with Sharing

by Rainer Grimm

From the article:

The patterns in this section are quite obvious, but I will present them with a short explanation for completeness. Let me start with Copied Value. If a thread gets its arguments by copy and not by reference, there is no need to synchronize access to any data. No data races and no lifetime issues are possible.

Data Races with References

The following program creates three threads. One thread gets its argument by copy, the other by reference, and the last by...

Concurrency Patterns -- Rainer Grimm

There are many well-established patterns used in the concurrency domain. They deal with synchronization challenges such as sharing and mutation but also with concurrent architectures. Today, I will introduce and dive deeper into them in additional posts.

Concurrency Patterns

by Rainer Grimm

From the article:

The main concern when you deal with concurrency is shared, mutable state or, as Tony Van Eerd put it in his CppCon 2014 talk “Lock-free by Example”: “Forget what you learned in Kindergarten (ie stop Sharing)”. A crucial term for concurrency is a data race. Let me first define this term.

  • Data race: A data race is when at least two threads access a shared variable simultaneously. At least one thread tries to modify the variable. If your program has a data race, it has undefined behavior. This means all outcomes are possible, so reasoning about the program makes no sense anymore.

A necessary condition for a data race is a mutable, shared...

Mutating Through a Filter -- Barry Revzin

MutatingThroughaFilter.pngNico Josuttis gave a talk recently that included an example and I wanted to explain what’s going on in this example, what the issue is, and what (if anything) is broken.

Mutating Through a Filter

by Barry Revzin

From the article:

As with a lot of my explanations, we have to start from the beginning.

The C++ iterator model has a number of iterator categories: input, forward, bidirectional, random access, and (since C++20) contiguous. This post only needs to consider the first two.

An input range (a range whose iterator is an input iterator) is a single-pass range. You can only ever call begin() one time on it. You can’t have multiple different input iterators into the same range - incrementing one immediately invalidates any existing copies.

How To Check If A Pointer Is In A Range Of Memory -- Raymond Chen

RaymondChenPic.pngC language was defined to cover a large range of computer architectures, including many which would be considered museum relics today. It therefore takes a very conservative view of what is permitted, so that it remains possible to write C programs for those ancient systems. (Which weren’t quite so ancient at the time.)

How To Check If A Pointer Is In A Range Of Memory

by Raymond Chen

From the article:

Suppose you have a range of memory described by two variables, say,

byte* regionStart;
size_t regionSize;

And suppose you want to check whether a pointers lies within that region. You might be tempted to write

if (p >= regionStart && p < regionStart + regionSize)

but is this actually guaranteed according to the standard?

Why is std::hardware_destructive_interference_size a Compile-time Constant ... -- Raymond Chen

C++17 added a new compile time constant std::hardware_destructive_interference_size which tells you (basically) the size of a cache line. The purpose of this is to allow you to lay out your structures in a way that avoids false sharing.

Why is std::hardware_destructive_interference_size a Compile-time Constant Instead of a Run-time Value?

by Raymond Chen

From the article:

C++17 added a new compile time constant std::hardware_destructive_interference_size which tells you (basically) the size of a cache line. The purpose of this is to allow you to lay out your structures in a way that avoids false sharing.¹

But how does the compiler know what the cache line size will be of the CPU the program will eventually be run on? Shouldn’t this be a run-time value instead of a compile-time value?

Well yes, the actual size of the cache line isn’t know until run-time, because it is only then that the program meets a CPU. But really, you want this to be a ...