What exactly does `threading=multi` do when compiling boost?

11.1k views Asked by At

I am not entirely sure what exactly the threading=multi flag does when building boost. The documentation says:

 Causes the produced binaries to be thread-safe. This requires proper
 support in the source code itself.

which does not seem to be very specific. Does this mean that the accesses to, for example, boost containers are guarded by mutexes/locks or similar? As the performance of my code is critical, I would like to minimize any unnecessary mutexes etc.

Some more details:

My code is a plug-in DLL which gets loaded into a multi-threaded third-party application. I statically link boost into the DLL (the plug-in is not allowed to have any other dependencies except standard Windows DLLs, so I am forced to do this).

Although, the application is multi-threaded, most of the functions in my DLL are only ever called from a single thread and therefore the accesses to containers need not be guarded. I explicitly guard the the remaining places of my code, which can be called from multiple threads, by using boost::mutex and friends.

I've tried building boost with both threading=multi and threading=single and both seem to work but I'd really like to know what I am doing here.

4

There are 4 answers

11
BeeOnRope On BEST ANSWER

No, threading=multi doesn't mean that things like boost containers will suddenly become safe for concurrent access by multiple threads (that would be prohibitively expensive from a performance point of view).

Rather, what it means in theory is that boost will be compiled to be thread aware. Basically this means that boost methods and classes will behave in a reasonable default way when accessed from multiple threads, much like classes in the std library. This means that you cannot access the same object from multiple threads, unless otherwise documented, but you can access different objects from multiple threads, safely. This might seem obvious, even without explicit support, but any static state used by the library would break that guarantee if not protected. Using threading=multi guarantees that any such shared state is property guarded by mutex or some other mechanism.

In the past similar arguments or stdlib flavors were available for the C and C++ std libraries supplied my compilers, although today mostly just the multithreaded versions are available.

There is likely little downside to compiling with threading=multi, given that only a limited amount of static state need to be synchronized. Your comment that your library will mostly only be called by a single thread doesn't inspire a lot of confidence - after all, those are the kinds of latent bugs that will cause you to be woken up at 3 AM by your boss after a night of long drinking.

The example of boost's shared_ptr is informative. With threading=single, it is not even guaranteed that independent manipulation of two shared_ptr instances, from multiple threads, is safe. If they happen to point to the same object (or, in theory, under some exotic implementations even if they don't), you will gen undefined behavior, because shared state won't be manipulated with the proper protections.

With threading=multi, this won't happen. However, it is still not safe to access the same shared_ptr instance from multiple threads. That is, it doesn't give any thread safety guarantees which aren't documented for the object in question - but it does give the "expected/reasonable/default" guarantees of independent objects being independent. There isn't a good name for this default level of thread-safety, but it is in fact what's generally offered all standard libraries for multi-threaded languages today.

As a final point, it's worth noting that Boost.Thread is implicitly always compiled with threading=multi - since using boost's multithreaded classes is an implicit hint that multiple threads are present. Using Boost.Thread without multithreaded support would be nonsensical.

Now, all that said, the above is the theoretical idea behind compiling boost "with thread support" or "without thread support" which is the purpose of the threading= flag. In practice, since this flag was introduced, multithreading has become the default, and single threading the exception. Indeed, many compilers and linkers which defaulted to single threaded behavior, now default to multithreaded - or at least require only a single "hint" (e.g., the presence of -pthread on the command line) to flip to multithreaded.

Apart from that, there has also been a concerted effort to make the boost build be "smart" - in that it should flip to multithreaded mode when the environment favors it. That's pretty vague, but necessarily so. It gets as complicated as weak-linking the pthreads symbols, so that the decision to use MT or ST code is actually deferred to runtime - if pthreads is available at execution time, those symbols will be used, otherwise weakly linked stubs - that do nothing at all - will be used.

The bottom line is that threading=multi is correct, and harmless, for your scenario, and especially if you are producing a binary you'll distribute to other hosts. If you don't special that, it is highly likely that it will work anyway, due to the build-time and even runtime heuristics, but you do run the chance of silently using empty stub methods, or otherwise using MT-unsafe code. There is little downside to using the right option - but some of the gory details can also be found in the comments to this point, and Igor's reply as well.

5
Sebastian On

The documentation says everything that is needed: this option ensures thread safety. That is, when programming in a multithreaded environment, you need to ensure certain properties like avoiding unrestricted access to e.g. variables.

I think, enabling this option is the way to go.

For further reference: BOOST libraries in multithreading-aware mode

7
Igor R. On

After some digging, it turns out that threading=single doesn't have much effect, as one would expect. In particular, it does not affect BOOST_HAS_THREADS macro and thus doesn't configure libraries to assume single threaded environment.

With gcc threading=multi just implies #define BOOST_HAS_PTHREADS, while with MSVC it doesn't produce any visible effect. Paricularly, _MT is defined both in threading=single and threading=multi modes.

Note however, that one can explicitly configure Boost libraries for single-threaded mode by defining the appropriate macro, like BOOST_SP_DISABLE_THREADS , BOOST_ASIO_DISABLE_THREADS, or globally with BOOST_DISABLE_THREADS.

2
lrineau On

Let's be concise. Causes the produced binaries to be thread-safe means that Boost code is adapted to that different threads can use different Boost objects. That means in particular that the Boost code will have a special care of the thread-safety of accesses to hidden global or static objects that might be use by the implementation of Boost libraries. It does not automatically allow different threads to use the same Boost objects at the same time without protection (locks/mutexes/...).

Edit: Some Boost libraries may document an extra thread-safety for specific functions or classes. For example asio::io_service as suggested by Igor R. in a comment.