Concurrency, part way too many - concurrency and the C runtime library.

For some reason, I can't seem to let this concurrency thing go, I keep on thinking of more and more relevant topics - there are a lot of issues surrounding concurrency.

I realized today that my concurrency series didn't talk at all about libraries and concurrency, and I want to address that issue in a couple of articles.  To help avoid feature creep, here a brief outline of the articles:

  1. Concurrency and the C runtime library
  2. Concurrency and the Win32 API set
  3. Concurrency and Windows (GDI and User)

That's it, I'm going to do my best to limit myself to those three articles - given my recent history on this topic, I'm not sure I'll succeed, but I'm going to try.

Ok, concurrency and the C runtime library (to be specific, the Microsoft C runtime library, YMMV for others)...

The C language specification is intentionally agnostic with respect to threads - there are no mentions of multithreading issues in the C specification at all, it's left up to the implementation.

To deal with potential multithreading issues, Microsoft produces three different versions of the C runtime library (actually there are 6 different versions - the three variants I'm describing come in debug and retail versions).  Those three versions are:

  • Non thread safe C runtime library (-ML)
  • Thread safe C runtime library (-MT)
  • DLL C runtime library (-MD)

The reason that there are three different libraries is that the performance characteristics of the three are different.  The non thread safe C runtime library is faster than the thread safe library because (a) it doesn't do locking internally, and (b) the C runtime library contains several functions (like strtok and errno) that are stateful (in other words, they rely on the results of previous operations that occurred on the thread).  The second is actually more important than the first - the overhead of locks for a single threaded application is quite small, but the stateful operations need to be implemented differently - they can't rely on global variables to hold their state, and instead have to rely on thread local storage.

The thread safe C runtime library exists to resolve the above issues - in the non thread-safe C runtime library, errno is a global variable, in the thread-safe C runtime library, it's a call to the function _errno() - the differences are hidden by a macro in the multithread case.  If you're writing a multithreaded application, it's critical that you use the thread safe C runtime library instead of the non thread safe library.

The third library (the DLL C runtime library) is also thread safe, and has the additional value of sharing code with all other applications that use the C runtime library in your process.  I've written about the DLL C runtime library before, so I'll just include that by reference.  I strongly recommend using the DLL C runtime library, simply because of the code sharing opportunities.

Microsoft has a set of articles describing multithreading and the C runtime library in more detail here.

Now, having said that, what about C++ and/or ATL?  In particular, what about the STL and ATL collection classes?  It turns out that they are explicitly NOT thread safe.  This is largely an artifact of their implementation - the ATL and STL template libraries are built in-line, they're not in a runtime library, and it's not trivial to be able to ensure concurrency (it's possible, for example, the iostream classes guarantee thread safety if you're using the thread safe runtime library, and the STL collections have specific semantics regarding thread safety that can be found here).

I've separately been informed that the ATL collection libraries for ATL 7.0 (CStringT, CAtlMap, CAtlMap, CAtlArray, CRBMap and CRBMultiMap at a minimum) have the same thread safety guarantees as the STL - a single object is safe for either one writer or many readers, but it's up to the user of the template to ensure that the guarantee is met.  And, as Pavel Lebedinsky pointed out the rationale for this can be found here.

 

Edit: Added clarification of the concurrency rules for ATL collections.