There is nothing intrinsic to a function that implies thread safety other than its awareness of what shared data may be modified, and how it protects that data. It is the data itself that exists in memory, and can be accessed from separate threads of execution, and not typically a function (unless you have a function whose code is modified after loading, but that's far more rare). In the case of C's
rand() function, its implementation accesses shared data, and does not protect against concurrent access. From the man page you mention: "The function
rand() is not reentrant, since it uses hidden state that is modified on each call."
If the language were written as you are describing, consider a situation where a utility function modifies
Foo. If this utility function were marked as thread-safe, it would have to needlessly take locks and protect data that was not (necessarily) shared. Additionally, you want to only block threads from concurrent access of the same memory location, not block access to all entry points in a function, which would be a bit clumsier when the generic function has to serialize execution, instead of something protecting a specific piece of data.