summaryrefslogtreecommitdiff
path: root/libc/src/errno/libc_errno.cpp
diff options
context:
space:
mode:
authorPetr Hosek <phosek@google.com>2024-07-13 10:52:42 -0700
committerGitHub <noreply@github.com>2024-07-13 10:52:42 -0700
commit69258491d201bf6d96a8a9bac2ea80a1b14d9cd4 (patch)
tree1c9ef8e2df479dd14c2d4f0d819d6350621c1d8b /libc/src/errno/libc_errno.cpp
parentd91ff3f2409a721b61b68c6a8438ea6c59323df8 (diff)
[libc] Support configurable errno modes (#98287)
Rather than selecting the errno implementation based on the platform which doesn't provide the necessary flexibility, make it configurable. The errno value location is returned by `int *__llvm_libc_errno()` which is a common design used by other C libraries.
Diffstat (limited to 'libc/src/errno/libc_errno.cpp')
-rw-r--r--libc/src/errno/libc_errno.cpp101
1 files changed, 72 insertions, 29 deletions
diff --git a/libc/src/errno/libc_errno.cpp b/libc/src/errno/libc_errno.cpp
index 341636f0495c..f7bd3a3b9eb4 100644
--- a/libc/src/errno/libc_errno.cpp
+++ b/libc/src/errno/libc_errno.cpp
@@ -7,47 +7,90 @@
//===----------------------------------------------------------------------===//
#include "libc_errno.h"
-#include "src/__support/CPP/atomic.h"
+#include "src/errno/errno.h"
#include "src/__support/macros/config.h"
-#ifdef LIBC_TARGET_ARCH_IS_GPU
-// LIBC_THREAD_LOCAL on GPU currently does nothing. So essentially this is just
-// a global errno for gpu to use for now.
-extern "C" {
-LIBC_THREAD_LOCAL LIBC_NAMESPACE::cpp::Atomic<int> __llvmlibc_errno;
-}
+// libc never stores a value; `errno` macro uses get link-time failure.
+#define LIBC_ERRNO_MODE_UNDEFINED 1
+// libc maintains per-thread state (requires C++ `thread_local` support).
+#define LIBC_ERRNO_MODE_THREAD_LOCAL 2
+// libc maintains shared state used by all threads, contrary to standard C
+// semantics unless always single-threaded; nothing prevents data races.
+#define LIBC_ERRNO_MODE_SHARED 3
+// libc doesn't maintain any internal state, instead the embedder must define
+// `int *__llvm_libc_errno(void);` C function.
+#define LIBC_ERRNO_MODE_EXTERNAL 4
+// libc uses system `<errno.h>` `errno` macro directly in the overlay mode; in
+// fullbuild mode, effectively the same as `LIBC_ERRNO_MODE_EXTERNAL`.
+#define LIBC_ERRNO_MODE_SYSTEM 5
+
+#ifndef LIBC_ERRNO_MODE
+#if defined(LIBC_FULL_BUILD) || !defined(LIBC_COPT_PUBLIC_PACKAGING)
+#define LIBC_ERRNO_MODE LIBC_ERRNO_MODE_THREAD_LOCAL
+#else
+#define LIBC_ERRNO_MODE LIBC_ERRNO_MODE_SYSTEM
+#endif
+#endif // LIBC_ERRNO_MODE
+
+#if LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_UNDEFINED && \
+ LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_THREAD_LOCAL && \
+ LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_SHARED && \
+ LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_EXTERNAL && \
+ LIBC_ERRNO_MODE != LIBC_ERRNO_MODE_SYSTEM
+#error LIBC_ERRNO_MODE must be one of the following values: \
+LIBC_ERRNO_MODE_UNDEFINED, \
+LIBC_ERRNO_MODE_THREAD_LOCAL, \
+LIBC_ERRNO_MODE_SHARED, \
+LIBC_ERRNO_MODE_EXTERNAL, \
+LIBC_ERRNO_MODE_SYSTEM
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Define the global `libc_errno` instance.
+Errno libc_errno;
+
+#if LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_UNDEFINED
+
+void Errno::operator=(int) {}
+Errno::operator int() { return 0; }
-void LIBC_NAMESPACE::Errno::operator=(int a) {
- __llvmlibc_errno.store(a, cpp::MemoryOrder::RELAXED);
+#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_THREAD_LOCAL
+
+namespace {
+LIBC_THREAD_LOCAL int thread_errno;
}
-LIBC_NAMESPACE::Errno::operator int() {
- return __llvmlibc_errno.load(cpp::MemoryOrder::RELAXED);
+
+extern "C" {
+int *__llvm_libc_errno() { return &thread_errno; }
}
-#elif !defined(LIBC_COPT_PUBLIC_PACKAGING)
-// This mode is for unit testing. We just use our internal errno.
-LIBC_THREAD_LOCAL int __llvmlibc_internal_errno;
+void Errno::operator=(int a) { thread_errno = a; }
+Errno::operator int() { return thread_errno; }
-void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_internal_errno = a; }
-LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_internal_errno; }
+#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_SHARED
+
+namespace {
+int shared_errno;
+}
-#elif defined(LIBC_FULL_BUILD)
-// This mode is for public libc archive, hermetic, and integration tests.
-// In full build mode, we provide the errno storage ourselves.
extern "C" {
-LIBC_THREAD_LOCAL int __llvmlibc_errno;
+int *__llvm_libc_errno() { return &shared_errno; }
}
-void LIBC_NAMESPACE::Errno::operator=(int a) { __llvmlibc_errno = a; }
-LIBC_NAMESPACE::Errno::operator int() { return __llvmlibc_errno; }
+void Errno::operator=(int a) { shared_errno = a; }
+Errno::operator int() { return shared_errno; }
-#else
-void LIBC_NAMESPACE::Errno::operator=(int a) { errno = a; }
-LIBC_NAMESPACE::Errno::operator int() { return errno; }
+#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_EXTERNAL
-#endif // LIBC_FULL_BUILD
+void Errno::operator=(int a) { *__llvm_libc_errno() = a; }
+Errno::operator int() { return *__llvm_libc_errno(); }
+
+#elif LIBC_ERRNO_MODE == LIBC_ERRNO_MODE_SYSTEM
+
+void Errno::operator=(int a) { errno = a; }
+Errno::operator int() { return errno; }
+
+#endif
-namespace LIBC_NAMESPACE_DECL {
-// Define the global `libc_errno` instance.
-Errno libc_errno;
} // namespace LIBC_NAMESPACE_DECL