The Problem
[The problem being addressed here is partially described in this issue also.]
Many libc functions set errno on error. If you are using LLVM’s libc as the only libc, then libc functions (which would all be from LLVM’s libc) set errno from LLVM’s libc. If you are using LLVM’s libc in the overlay mode, then libc functions from LLVM’s libc set errno from the system libc. Which means that in both these cases, the global errno (the errno of the process thread executing the function) is affected. The same happens even with unit tests and integration tests. In case of the unit tests testing functions from LLVM’s libc in the overlay mode, the global errno is actually the errno of the system libc. Consequently, if a libc function under test sets errno to an error value, then it affects the errno of the thread executing the function in the unit test’s process. This has the potential to interfere with the operation of the unit test infrastructure running on that thread. This RFC proposes a solution to fix this problem. The goal is to improve the hermeticity of unit tests with respect errno - we want to be able to test that the function under test sets the errno to the expected value, but we do not want the function to affect the global errno and potentially interfere with the unit test infrastructure.
Scope
- The solution should be able to handle the full build mode and the overlay build mode.
- The solution should work for the different flavors in which
errnomacro is defined by the system libc. There are likely two variations that have to be handled here:- The system libc defines the
errnovia a helper function, typically*__errno_location(). - The system libc defines the
errnoas extern thread-local variable.
- The system libc defines the
Proposal
The core of the proposal is to use a libc internal macro named libc_errno to set errno in libc function code. The same macro should also be used to test the error values in tests. The essential idea is to point libc_errno to the production/global errno or an internal errno based on LIBC_COPT_PUBLIC_PACKAGING. It is best illustrated by this patch.
Runtime overhead
The proposal does not add any overhead to the user code or libc internal code.
Feedback on Programming errors
If libc developers accidentally use errno instead of libc_errno in the libc runtime code or tests, the above solution will trigger a linker error when building tests in full build mode. The public/global errno variable is completely excluded in test binaries. So, using errno instead of libc_errno from libc internal code should lead to undefined symbol errors when building tests in the full-build mode.
Setting errno from helper functions defined in object libraries
Similar to accidental usage of the errno macro instead of the libc_errno macro in libc internal code, setting errno via errno or libc_errno in helper object libraries will trigger undefined symbol error. Which effectively means that the above proposal disallows setting errno from helper functions defined in helper object libraries. Setting errno in general is not idiomatic C++ anyway. So, instead of setting errno directly, helper functions should return error values on error, or return compound objects like optional or ErrorOr or expected as appropriate. The setting of errno should happen only in the main libc function, ideally just before returning from the function.
Rolling out the above proposal
We can do a single-shot switch of the entire libc directory to the above proposal. However, there are a few preparatory cleanups that need to done either before the switch or after the switch. So, I propose to do the switch in multiple steps.
- First step is to land this patch with a slight change - allow use of
libc_errnoanderrnodirectly from libc internal code. This will allow us to gradually, in many small patches, switch over to the new way of accessingerrnofrom libc internal code. - To address this issue, a large number of helper functions have been modified to return idiomatic C++ types instead of setting
errnodirectly. However, many more helper functions and helper object libraries have to be cleaned up. As the second step, we will perform such cleanups. - In many places, in parallel to the cleanups in step #2, we can also switch over direct references to
errnotolibc_errno. - Once all the cleanups and switchover to
libc_errnoare complete, we will disallow settingerrnodirectly from libc internal code.