The Posix Standard says

"3.407 Thread-Safe A thread-safe function can be safely invoked concurrently with other calls to the same function, or with calls to any other thread-safe functions, by multiple threads. Each function defined in the System Interfaces volume of POSIX.1-2017 is thread-safe unless explicitly stated otherwise. Examples are any 'pure' function, a function which holds a mutex locked while it is accessing static storage, or objects shared among threads."

Doesn't that indicate that a libc implementation is allowed to be written so that the execution of a non-thread-safe function that isn't mutex-protected may break the thread-safety of every allegedly thread-safe function concurrently executing?

It seems unlikely that the execution of the non-thread-safe mathematical function lgamma() would somehow interfere with the execution of a thread-safe totally unrelated function in another thread, but it appears to me that the Standard allows it, so that 'pure' functions can become thread-unsafe. Is this the intent?

More likely would be that mblen() could destroy the thread-safety of a concurrently executing mbrlen(). But that would severely restrict the utility of threads. It would mean that I can't add a new thread to an existing application without close inspection of the code because of this possibility of bleed through.

Or is this just poor wording in the Standard (it wouldn't be the first time)?

This is theoretical; I haven't tried to demonstrate this, and I hope I'm wrong

Question edited to add what's below:

I think I didn't ask this clearly enough. So I'll rephrase my question to use a specific example.

Suppose there are two threads concurrently executing the mbrlen() function, both with the ps arg set to not NULL. The Standard requires a conformant libc implementation to be written in such a way that neither function call interferes with that of the other thread.

Now suppose that the situation is changed so that one of the threads is using the mblen() function instead. This is the only difference from the above situation. May a libc implementation that is compliant with the Standard be written such that the mblen() call could cause the other thread's mbrlen() call to return the wrong result?

Answer "No" if the Standard prohibits a compliant implementation from having any possibility of such interference. If you answer no, please cite the text in the Standard that indicates such a prohibition.

Otherwise answer "Yes". But the implication of this answer is that no thread need be safe unless all threads are safe; no function call need be thread-safe unless all other concurrent function calls are thread-safe.

I need to emphasize that I'm not referring to how libc implementations actually work, but the legalese of the Standard. I have written code assuming that the answer is "No" and has millions of instances executing around the world in every instant of every day. Applications are free to add threads executing whatever they want, and we've had no reports of this kind of error. Maybe some heisenbugs are a result of this; maybe nobody adds interfering threads. But I do think this is pretty good evidence that libc applications don't take advantage of this loophole, if indeed it is a loophole.

3

There are 3 best solutions below

1
Joshua On

No, it does not. It means calling two such functions from two threads at the same time breaks the thread safety of everything.

However there are a few exceptions. chdir(), setenv(), and unsetenv() are super not-thread-safe and can cause problems for thread-safe functions.

chdir(): most FS functions won't be happy with changing the current directory in the middle.

setenv() and unsetenv(): getenv() is listed as thread safe but isn't if one of these is being called from another thread.

The standard appears to say something really dumb though: I don't see why you can't call strtok() from one thread and lgamma() from another but it reads like you can't.

I went back through the question and read up on mblen() and mbrlen() more thoroughly. It's not as bad as it sounds. mbrlen() is definitely thread safe whenever its third argument is not NULL. It's a pretty good guess however that mblen(s, n) is implemented as mbrlen(s, n, NULL) in older implementations and so the standard leaves open that possibility; but later on the documentation for mbrlen() was updated to remove this case. (The modern definition reads "The system shall behave as though no [standard library] function calls mbrlen".)

0
Serge Ballesta On

The standard gives rules to both compiler implementers and normal programmers. What is meant is that a library implementor has not to care for independance of unrelated non thread-safe functions when called from different threads. As a result, a cautious programmer should not make any assumption about it, even if the risk seems low. Even controlling the library source to make sure that no problem can occur would only be relevant for one single version of one specific implementation.

Said differently, programmers can only make such assumptions at their own risk. Caveat emptor...

0
Luis Colorado On

Doesn't that indicate that a libc implementation is allowed to be written so that the execution of a non-thread-safe function that isn't mutex-protected may break the thread-safety of every allegedly thread-safe function concurrently executing?

No. Thread safety is a property of a function. It means that you can use it (well with some care, see below about signal handlers that are not executed in a separate context) in a multithreading environment, without caring about locks. Using it, will not break the thread safety of your functions and you will not need to provide extra locking code to cope with it. To break the thread safety of a function you need to modify it to take the property out.

Let me show you this with an example: The printf() implementation of glibc claims to be thread safe. This is a hard assumption as far as it must manage a static buffer in the FILE structure that will trash if two instances of printf() happen to act into the same FILE structure. Thread safety of printf() this time falls into the group you shown of functions that do internally the proper locking to disallow concurrent access to the i/o buffers the FILE structure must handle.

Two such threads that happen to call printf() at the same time, can continue to do, as much as the buffer is locked while one of them runs, and the other instances must wait for the buffer to be unlocked. This can slow down you multithread application if you abuse of printf(), although.

Some other case of thread safe functions are the ones that are reentrant. A reentrant function is a function that can be called recursively, as it uses no global state at all, or it is passed to the function by reference, in a way that warrants that never two recursive instances will be accessing the same data. This concept is a bit different (and more restrictive) than Thread safety, as this impedes a thread safe function to be used in a signal handler, for example. Just imagine your code is in the middle of a printf() call when you call the signal handler (this can easily happen) imagine that the call that was interrupted by the signal handler is holding the lock when the signal handler was called and it gets again into printf() and tries to lock the buffer but it has to wait, because the lock is already handled. As the buffer state is in the middle of a transaction the second call must be put to wait to finish, but this will never happen because there's no other thread, the signal handler must execute to its end for the first call to be permitted to continue and free the lock. This is the reason for which some operating systems (e.g. FreeBSD) implement user signal handlers in an specific context, allowing the signal handler function to be locked and the original code to continue (the signal handler will be unlocked and rescheduled when the first unlocks the buffer and everything will be ok)