Goodbye (And Good Riddance) to -mt -and -D_REENTRANT |
Ali Bahrami Tuesday December 22, 2015
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:
For a few years, -mt was a nice and simple solution, but then, the world changed.-mt Use this option to compile and link multithreaded code. The -mt option assures that libraries are linked in the appropriate order.
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:
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.-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.
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:
If you're building code on Solaris 11 Update 4 or newer, you should drop those options from your makefiles, and simplify.
[28] Weak Filters | [30] New CRT Objects |