Thursday, January 9, 2014

FeedaMail: Comments for Sutter̢۪s Mill

feedamail.com Comments for Sutter's Mill

Comment on Reader Q&A: Acquire/release and sequential consistency by Tony Van Eerd

I think the important case is when b and c are global variables, possibly side-by-side in memory.

Herb says “it's fine because all modern processors have single-byte reads so no need even to inject alignment/padding”

Bjarne says (in the FAQ) “However, most modern processors cannot read or write a single character, it must read or write a whole word”

I think the answer @Fernando is looking for is that, with concurrency now in the standard, it is up to the compiler to make sure it works. Typically that means either the processor can do single-byte reads _or_ the chars are padded/aligned. Or whatever else the compiler thinks is best (but almost definitely not locks, I hope!).

I wonder what a compiler would do on a machine that doesn’t have single-byte reads, but you set alignment/packing to 1?…

Read More »

Comment on GotW #95: Thread Safety and Synchronization by Ralph Tandetzky

1. What is a race condition, and how serious is it?

Well, a race condition is the unpredictable behavior of a piece of software due to a dependence on sequence and timing of different threads or processes. A data race is a race condition that follows from a write access to a non-atomic variable and access by an other thread (read or write) to that same variable. Access to atomic variables from multiple threads can still lead to race condition. However, as long as the end result of the program does not depend on it we’re fine and the program is correctly synchronized.
A data race as defined above is much more dangerous since it implies undefined behavior. And undefined behavior means that your program may actually do **anything** including sending an e-mail to your boss that you quit or starting world war III against mankind. So a data race is indeed very serious!

2. What is a correctly synchronized program? How do you achieve it? Be specific.

A function is correctly synchronized, if the result of it does not depend on timing of threads or processes. A program is correctly synchronized if it consists of correctly synchronized functions only. That means that at least shared access to a common data amoung multiple threads must be safe: either the variables are atomic or it is ensured that while one thread writes to a location there are no accesses to the same location from other threads at the same time. Typically this is achieved by protecting the data by a mutex.

3. The types in (a)-(d) are not thread safe in the sense that concurrent read and write access is guaranteed to be without data race. Therefore, the program will not be correctly synchronized in these cases.

The types in (e)-(g) are thread safe. Hence, there will be no data race as defined above. However, the execution of the program may still depend on the order in which the variable is modified and read by the two functions. Therefore, the program might be correctly synchronized but it is also possible that the program is not correctly synchronized and there’s a latent hard to detect bug. (Even though there’s no undefined behavior.)

4. (a) What is the normal external synchronization responsibility of code that owns and uses a given shared variable?

The code must make sure that whenever there is a write to that variable there are no concurrent read or write accesses to it by other threads. Concurrent read accesses are allowed.

4. (b) What is the "basic thread safety guarantee" that all types must obey to enable calling code to perform normal external synchronization?

All access to a const object of that type must be thread-safe. Furthermore, if there are multiple instances of that type then it must be possible to access (read or write) different objects concurrently as long as each write to each object is mutually exclusive. In other words: As long as one thread has write access to an object there must be no other accesses from other threads to that objects, but other threads may access other objects at the same time.

This is what the standard library guarantees for all its types and functions (with only a few exceptions like rand()). The standard library also expects this from user types, for example when used as template arguments.

4. (c) What partial internal synchronization can still be required within the shared variable's implementation?

If functions of a type access static or global data, then this access must be protected in order to provide the basic thread safety guarantee. Also different instances of a type may internally refer to some shared state. Access to that shared state must also be protected, if writes are possible. An example for this would be shared_ptr. When copying a shared_ptr the two resulting smart pointers will internally refer to the same object **and** the same reference counter. It is expected from the user to protect accesses to the pointee, but accesses to the reference counter must be synchronized internally. That’s the reason shared_ptr may incur a performance penalty compared to unique_ptr which is as efficient as a raw pointer in most cases.

5. What types should be fully internally synchronized, and why?

Types that are used in order to communicate between different threads or that enable synchronization between threads such as mutexes, condition variables, futures and concurrent containers should be internally synchronized. For almost all other types internal synchronization is not necessary.

There’s the so called monitor pattern which in essence turns a type providing the basic thread safety guarantee into a thread-safe type by adding a mutex and protecting every access to an instance by locking that mutex. This serializes the data accesses. Sometimes this kind of behavior is safer, but not very useful. An example are streams: E. g. writing to cout is thread-safe, but the letters arriving in the standard output might be interleaved. (That’s a race condition, but no data race as defined above. The program is not correctly synchronized.) That’s a mess with different threads and external synchronization is the only thing that really helps this issue.

For normal types there is no use in making them thread-safe, but it is usually better to let the caller decide on how to synchronize access. Most types don’t need to know about multi-threading. It a bit like exceptions. Most types don’t need to worry about exceptions, they let the caller take care of them. Don’t try to give every type the responsibility of synchronizing itself. Most often it is not necessary and it only slows down the program and external synchronization, if at all necessary, is the better alternative.

From my experience hardly any types need internal synchronization. Exceptions are typically types that are used for synchronization or types that have only one global instance (such as the standard output stream or particular pieces of hardware).

Read More »

Comment on GotW #95: Thread Safety and Synchronization by nosenseetal

@Bartosz nice A, I think Herb in 5 is aiming for stuff from his talk you dont know const and mutable…
and his M&M rule, mutexes and mutable, aka you should make your mutex mutable so external callers can call const methods although those methods modify the mutex by locking it…

Read More »
 
Delievered to you by Feedamail.
Unsubscribe