You can expect any reasonable modern implementation to have a thread-safe errno. It would be nearly worthless other way. In any case, the biggest problem in using errno is that the standard only defines 2 error codes (EDOM and ERANGE), so if you wish to extend its functionality with new codes, you'll have to be careful so that they don't clash with the ones already defined in the system.
Originally Posted by Memloop
There is a simple way to define your own errno equivalent. It works for MSVC and gcc only, but those 2 cover like 90% of the used compilers. With this method, you can have a per-thread variable and easily define your error codes:
For any other compiler, if it's on an x86 and has inline assembler, you can emulate the above using a segment register.
extern __thread int my_errno;
extern __declspec(thread) int my_errno;
Yes, I prefer to handle errors that way myself. I don't think it's hard to come up with values that can be used as error codes. In your strcmp example, it's logical to assume that the return value is computed as the difference between the characters that compose the strings, and given that chars are almost always one byte in size, there's no way that the expression "char1 - char2" can lead to a value of SHRT_MIN or INT_MIN, for example, so those could be used as error codes.
This site is advocating that you shouldn't use in-band error indicators at all, and instead always have functions return error indicators only. That would make the error handling more thread safe, but on the other hand it would lead to more contrived code. For example, if you were to rewrite a simple function like strcmp this way, you would need to always create an additional value to hold the result.
I prefer to have the function verify the integrity of its arguments and return with an error code if something's wrong. It's made my life easier and has helped me with debugging. I can see why others would disagree, though.
Another question is if functions should always do as much error checking as they can, or leave some of it to the caller. For example, should the caller or function check if a parameter is a null pointer or not (in those cases when it's not allowed to be NULL)?