Goodbye (And Good Riddance) to -mt -and -D_REENTRANT

Ali Bahrami — Tuesday December 22, 2015

Surfing with the Linker-Aliens

One of the nice simplifications in Solaris 11 Update 4 is that it is no longer necessary to pass the -mt option to the compilers, or to set -D_REENTRANT when building threaded code. Nothing will stop you from doing that, but they no longer do anything, and can be removed. This is going to simplify documentation, and eliminate potential bugs when making system calls.

If you're building on Solaris 11.3 or older, you do still need to set -D_REENTRANT, but -mt is not needed.

More than once over the years, I've had to dig into the documentation to remind myself out what these settings do. We'll be tripping over them in Makefiles for years to come, and I expect official documentation for them to fade away over time. This article is for those of us in the future who enter searches into Google like "Solaris _REENTRANT what?".

Originally, SunOS, as with all versions of Unix at the time, was a single threaded system. Processes had a single thread of execution, and in fact, the notion of "thread of execution" didn't really exist. That changed during the 1990's, as Sun started producing symmetric multi-processor systems. Threading was a hot topic, and Sun was a leader in that space.

In those early systems, threading was an exotic technique that most processes didn't use. As such, all the thread APIs were found in a library, libthread, rather than in the core libc library. libc was modified to use locks to allow threads safe access to its routines, but only if the process was linked against libthread. If libthread was not present, then libc would run in the traditional unlocked manner.

The _REENTRANT macro dates from that era. When Unix system calls fail, they generally return a value that indicates failure, and set a global variable named errno with a code that describes the specific failure. In a threaded world, errno needs to be a per-thread variable rather than a single global variable. Compiling your code with -D_REENTRANT would trigger #ifdefs in the system header files to cause the name errno to resolve to a per-thread variable. A common pitfall is to employ threads in your code, and not compile it with -D_REENTRANT. All of your threads then set a single global errno, leading to all sorts of potential race conditions when handling errors. As a result, things "mostly work", with rare unexplained glitches.

At the same time, as a convenience to programmers, the compilers added the -mt option, intended to be a single option programmers could set to build code for threaded use. -mt set _REENTRANT, and also pulled in libthread, as described in the compiler manpages:

       -mt    Use this option to compile and link multithreaded
              code. The -mt option assures that libraries are
              linked in the appropriate order.
For a few years, -mt was a nice and simple solution, but then, the world changed.

The first change was caused by Posix Threads, which were standardized a few years after Sun had shipped libthread and the APIs it delivers. The two APIs are extremely similar, and as the industry adopted pthreads, Sun made them available as well, providing them in libpthread. The new, slightly more complicated rule for building multithreaded code became that you set -D_REENTRANT, and link to one of libthread or libpthread. The documentation for -mt changed to:

       -mt    Use this option to compile and link multithreaded
              code. The -mt option assures that libraries are
              linked in the appropriate order.

              If you are using POSIX threads, you must link with
              the options -mt -lpthread.
So much for a single option: -mt only ever did 2 things, and now one of them (-lthread) was often the wrong thing, and needed to be augmented manually by the user with -lpthread. That was slightly annoying, but it worked, and things carried on for a few more years.

The next, far bigger, change was caused by the great thread unification that was delivered by Roger Faulkner in Solaris 10. By the Solaris 10 timeframe, threads had become pervasive in Solaris, used not just by applications, but also in libraries. You might not have threaded your code, but your program might still have multiple threads running. The old model of threaded and non-threaded programs, as determined by the programmer building the program, no longer made sense. All processes are potentially threaded, all the time. Thread unification in Solaris 10 changed a number of things:

As a result, things are vastly simpler, easier, and more reliable now. The minor exception to that is that up until Solaris 11 Update 4, it was still necessary to understand -D_REENTRANT, and -mt. Roger again stepped up, and delivered the final coda to thread unification. In Solaris 11 Update 4, it is no longer necessary to set -D_REENTRANT. errno is now always a per-thread value regardless of whether -D_REENTRANT is set or not. And since the only useful thing done by -mt was to set -D_REENTRANT, it is no longer necessary to set -mt either.

If you're building code on Solaris 11 Update 4 or newer, you should drop those options from your makefiles, and simplify.

Surfing with the Linker-Aliens

Published Elsewhere

Surfing with the Linker-Aliens

[28] Weak Filters
Blog Index (ali)
[30] New CRT Objects