Project

General

Profile

Actions

Feature #13842

open

thread-local errno is all you need

Added by Joshua M. Clulow 15 days ago. Updated 15 days ago.

Status:
New
Priority:
Normal
Assignee:
-
Category:
lib - userland libraries
Start date:
Due date:
% Done:

0%

Estimated time:
Difficulty:
Medium
Tags:
Gerrit CR:

Description

Many modern C/C++ programs use threads. The mechanics of errno in threaded programs is slightly more complex than in the pre-thread era: while errno must appear to C source as a global variable, it must in reality have a per-thread value.

In /usr/include/errno.h, we define an errno macro which is implemented in terms of a function, ___errno(), which returns a pointer to the per-thread version of errno that software should be using:

#if defined(_REENTRANT) || defined(_TS_ERRNO) || _POSIX_C_SOURCE - 0 >= 199506L
extern int *___errno();
#define errno (*(___errno()))
#else
extern int errno;
/* ANSI C++ requires that errno be a macro */
#if __cplusplus >= 199711L
#define errno errno
#endif
#endif  /* defined(_REENTRANT) || defined(_TS_ERRNO) */

By default, neither _REENTRANT nor _TS_ERRNO are defined in the compilation environment, so it is unfortunately very easy for a program that uses threads to be miscompiled to use the global errno. This can result in failures with apparently spurious "Error 0" conditions in messages when software attempts to use, e.g., strerror().

In the file lib/libc/port/threads/thr.c, in libc_init(), we set the per-thread location for errno to the classical global errno (which we cannot remove for binary compatibility reasons):

        self->ul_errnop = &errno;

In all other threads, we set it to a location in the thread ulwp_t structure:

        ulwp->ul_errnop = &ulwp->ul_errno;

While the threads page in the manual does correctly direct people to the use of -D_REENTRANT for multi-threaded programs, it seems that at a minimum we could stop requiring it to get the thread-local errno. We could replace the entire definition in the header with an unconditional:

extern int *___errno();
#define errno (*(___errno()))

___errno() would continue to return the location of the global errno for the first thread, as it does today, and this change would have no impact on the ABI. It is unlikely that any correct C/C++ program would stop building as a result of this change.

Actions #1

Updated by Joshua M. Clulow 15 days ago

  • Description updated (diff)
Actions #2

Updated by Joshua M. Clulow 15 days ago

  • Description updated (diff)
Actions #4

Updated by Joshua M. Clulow 15 days ago

In addition to errno, there is a similar global t_errno variable defined in other headers (tiuser.h, xti.h, etc) that we should likely fix at the same time and in the same way:

#if defined(_REENTRANT) || defined(_TS_ERRNO) || \
        _POSIX_C_SOURCE - 0 >= 199506L
extern int      *__t_errno(void);
#define t_errno (*(__t_errno()))
#else
extern int t_errno;
#endif  /* defined(_REENTRANT) || defined(_TS_ERRNO) */
Actions

Also available in: Atom PDF