10

Background: I create many small utilities for some very specialised data processing. Often, I am the only user. I don't even think about multi-threaded programming, because the run-time performance is by far sufficient for my use cases. The critical resource is my programming time. So I want to avoid any extra effort required for multi-threaded programming.

However, it seems there is a risk, that my source code gets executed in a multi-threaded context, when I reuse my code in future.

According to CppCoreGuidelines :

Be careful: there are many examples where code that was “known” to never run in a multi-threaded program was run as part of a multi-threaded program. Often years later. Typically, such programs lead to a painful effort to remove data races. Therefore, code that is never intended to run in a multi-threaded environment should be clearly labeled as such and ideally come with compile or run-time enforcement mechanisms to catch those usage bugs early.

Most of the suggestions in the same source actually get me started with multi-threaded programming. The one suggestion, which I prefer to follow says:

Refuse to build and/or run in a multi-threaded environment.

So my question is, how do I do that? E.g. is there an include file, #pragma or so to ensure single threaded build / execution of everything within a source file?

Kağan Kayal
  • 2,303
  • 1
  • 19
  • 30
  • I don't think there is a preprocessor directive to do that. – Ron Apr 24 '18 at 09:02
  • @Ron I think he means that if he build a library that is later use by other people, to forbidden using the library if threading libs are included. – Brighter side Apr 24 '18 at 09:03
  • @Ron I would like to follow this advice in these CppCoreGuidelines saying "Refuse to build and/or run in a multi-threaded environment.". But I don't know how. I can't rely on compiler switches, becauseI have no control over them in infuture applications. Additionally, I end up linking libraries, which were configured as multi-threaded and I end up linking with pthread. So, bit by bit, there is a risk that my source code could leek into a multi-threaded environment and I want the compiler to enforce the programmers either to make the code thread safe or to leave it. – Kağan Kayal Apr 24 '18 at 09:05
  • 5
    Those suggestions make little sense because: 1) execution environments are mostly multithreaded these days 2) c++ code is not thread safe by default. If someone uses not thread safe code in thread safe manner it would be just a shot in the leg. – user7860670 Apr 24 '18 at 09:06
  • @KağanKayal That particular example uses the [std::async](http://en.cppreference.com/w/cpp/thread/async) function which might or might not spawn a thread. – Ron Apr 24 '18 at 09:06
  • 1
    just my opinion: anything that is not explicitly documented as therad-safe has to be assumed to be not thread-safe. So it isnt you who writes those utilities that needs to take care, but it is you who uses the utlities (maybe in a multithreaded context) to make sure to use them correctly – 463035818_is_not_an_ai Apr 24 '18 at 09:24
  • 2
    After re-reading that section I would rather say that it is mostly focusing on the problem of hidden global state. Even though it is a quite common source of thread safety violations this code can be still used in multithreaded environment (and can be used safely if user implements extra synchronization) so that suggestion to "refuse to build" seems to be an overkill. – user7860670 Apr 24 '18 at 09:27

1 Answers1

7

With g++/gcc compiling and linking multithreaded code requires using -pthread compiler and linker option. This option sets _REENTRANT macro which you can inspect at compile time:

$ c="g++ -E -dD -xc++ /dev/null"
$ diff <($c) <($c -pthread)
389a390
> #define _REENTRANT 1

Contrary to popular belief, using -lpthread linker option is unnecessary and insufficient to correctly build a multi-threaded program.


Microsoft Visual Studio sets _MT macro for a multi-threaded build, IIRC.


Boost library does the following:

// Turn on threading support if the compiler thinks that it's in
// multithreaded mode.  We put this here because there are only a
// limited number of macros that identify this (if there's any missing
// from here then add to the appropriate compiler section):
//
#if (defined(__MT__) || defined(_MT) || defined(_REENTRANT) \
    || defined(_PTHREADS) || defined(__APPLE__) || defined(__DragonFly__)) \
    && !defined(BOOST_HAS_THREADS)
#  define BOOST_HAS_THREADS
#endif

So that you can #include <boost/config.hpp> and then inspect the value of BOOST_HAS_THREADS macro.


The following causes a compilation error if it is built in multi-threaded mode:

#if defined(BOOST_HAS_THREADS) || defined(_REENTRANT) || defined(_MT)
#error This code is single-threaded only.
#endif
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271