[libc-commits] [libc] 658c84e - [libc] Add GNU extension functions pthread_setname_np and pthread_getname_np.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Mon Aug 1 18:57:12 PDT 2022
Author: Siva Chandra Reddy
Date: 2022-08-02T01:57:03Z
New Revision: 658c84e4158fc185ff68e8399d0a149bde98a489
URL: https://github.com/llvm/llvm-project/commit/658c84e4158fc185ff68e8399d0a149bde98a489
DIFF: https://github.com/llvm/llvm-project/commit/658c84e4158fc185ff68e8399d0a149bde98a489.diff
LOG: [libc] Add GNU extension functions pthread_setname_np and pthread_getname_np.
Reviewed By: michaelrj, lntue
Differential Revision: https://reviews.llvm.org/D130872
Added:
libc/src/pthread/pthread_getname_np.cpp
libc/src/pthread/pthread_getname_np.h
libc/src/pthread/pthread_setname_np.cpp
libc/src/pthread/pthread_setname_np.h
libc/test/integration/src/pthread/pthread_name_test.cpp
Modified:
libc/config/linux/x86_64/entrypoints.txt
libc/spec/gnu_ext.td
libc/spec/posix.td
libc/spec/spec.td
libc/src/__support/CPP/stringstream.h
libc/src/__support/threads/CMakeLists.txt
libc/src/__support/threads/linux/CMakeLists.txt
libc/src/__support/threads/linux/thread.cpp
libc/src/__support/threads/thread.h
libc/src/pthread/CMakeLists.txt
libc/test/integration/src/pthread/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 44191e1d9878b..07e57d667e670 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -251,8 +251,10 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.pthread.pthread_create
libc.src.pthread.pthread_detach
libc.src.pthread.pthread_equal
+ libc.src.pthread.pthread_getname_np
libc.src.pthread.pthread_join
libc.src.pthread.pthread_self
+ libc.src.pthread.pthread_setname_np
libc.src.pthread.pthread_mutex_destroy
libc.src.pthread.pthread_mutex_init
libc.src.pthread.pthread_mutex_lock
diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td
index 1985200fb9d34..ec22e8e0ee590 100644
--- a/libc/spec/gnu_ext.td
+++ b/libc/spec/gnu_ext.td
@@ -111,10 +111,30 @@ def GnuExtensions : StandardSpec<"GNUExtensions"> {
]
>;
+ HeaderSpec PThread = HeaderSpec<
+ "pthread.h",
+ [], // Macros
+ [], // Types
+ [], // Enumerations
+ [
+ FunctionSpec<
+ "pthread_setname_np",
+ RetValSpec<IntType>,
+ [ArgSpec<PThreadTType>, ArgSpec<ConstCharPtr>]
+ >,
+ FunctionSpec<
+ "pthread_getname_np",
+ RetValSpec<IntType>,
+ [ArgSpec<PThreadTType>, ArgSpec<CharPtr>, ArgSpec<SizeTType>]
+ >,
+ ]
+ >;
+
let Headers = [
CType,
FEnv,
Math,
+ PThread,
StdIO,
String,
];
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 4a8d08d9f7ebe..a6560147a7b3a 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -50,7 +50,6 @@ def POSIX : StandardSpec<"POSIX"> {
ConstType ConstPThreadMutexTPtr = ConstType<PThreadMutexTPtr>;
ConstType ConstRestrictedPThreadMutexTPtr = ConstType<RestrictedPThreadMutexTPtr>;
- NamedType PThreadTType = NamedType<"pthread_t">;
PtrType PThreadTPtr = PtrType<PThreadTType>;
RestrictedPtrType RestrictedPThreadTPtr = RestrictedPtrType<PThreadTType>;
diff --git a/libc/spec/spec.td b/libc/spec/spec.td
index d609f8e04ea3e..3e5cbd2fedbff 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -107,6 +107,8 @@ def FILE : NamedType<"FILE">;
def FILEPtr : PtrType<FILE>;
def FILERestrictedPtr : RestrictedPtrType<FILE>;
+def PThreadTType : NamedType<"pthread_t">;
+
//added because __assert_fail needs it.
def UnsignedType : NamedType<"unsigned">;
diff --git a/libc/src/__support/CPP/stringstream.h b/libc/src/__support/CPP/stringstream.h
index 6b2ff881d96f3..2fb467061f27a 100644
--- a/libc/src/__support/CPP/stringstream.h
+++ b/libc/src/__support/CPP/stringstream.h
@@ -85,6 +85,8 @@ class StringStream {
// Return true if any write operation(s) failed due to insufficient size.
bool overflow() const { return err; }
+
+ size_t bufsize() const { return data.size(); }
};
} // namespace cpp
diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index 9bfcee4a3ace9..41d0c47eb5c57 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -25,6 +25,8 @@ add_header_library(
DEPENDS
libc.src.__support.common
libc.src.__support.CPP.atomic
+ libc.src.__support.CPP.string_view
+ libc.src.__support.CPP.stringstream
)
if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.thread)
diff --git a/libc/src/__support/threads/linux/CMakeLists.txt b/libc/src/__support/threads/linux/CMakeLists.txt
index 5c7eb3ad8683b..d26b130d580e0 100644
--- a/libc/src/__support/threads/linux/CMakeLists.txt
+++ b/libc/src/__support/threads/linux/CMakeLists.txt
@@ -30,6 +30,8 @@ add_object_library(
libc.include.sys_syscall
libc.src.__support.CPP.atomic
libc.src.__support.CPP.error
+ libc.src.__support.CPP.stringstream
+ libc.src.__support.CPP.string_view
libc.src.__support.threads.thread_common
COMPILE_OPTIONS
-O3
diff --git a/libc/src/__support/threads/linux/thread.cpp b/libc/src/__support/threads/linux/thread.cpp
index 83c85f9b5128b..b1b9634119ea4 100644
--- a/libc/src/__support/threads/linux/thread.cpp
+++ b/libc/src/__support/threads/linux/thread.cpp
@@ -8,8 +8,10 @@
#include "src/__support/threads/thread.h"
#include "config/linux/app.h"
+#include "src/__support/CPP/StringView.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/error.h"
+#include "src/__support/CPP/stringstream.h"
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
#include "src/__support/threads/linux/futex_word.h" // For FutexWordType
@@ -17,7 +19,10 @@
#include <arm_acle.h>
#endif
+#include <errno.h>
+#include <fcntl.h>
#include <linux/futex.h>
+#include <linux/prctl.h> // For PR_SET_NAME
#include <linux/sched.h> // For CLONE_* flags.
#include <stdint.h>
#include <sys/mman.h> // For PROT_* and MAP_* definitions.
@@ -33,6 +38,7 @@ static constexpr long MMAP_SYSCALL_NUMBER = SYS_mmap;
#error "SYS_mmap or SYS_mmap2 not available on the target platform"
#endif
+static constexpr size_t NAME_SIZE_MAX = 16; // Includes the null terminator
static constexpr size_t DEFAULT_STACK_SIZE = (1 << 16); // 64KB
static constexpr uint32_t CLEAR_TID_VALUE = 0xABCD1234;
static constexpr unsigned CLONE_SYSCALL_FLAGS =
@@ -278,4 +284,95 @@ bool Thread::operator==(const Thread &thread) const {
return attrib->tid == thread.attrib->tid;
}
+static constexpr cpp::StringView THREAD_NAME_PATH_PREFIX("/proc/self/task/");
+static constexpr size_t THREAD_NAME_PATH_SIZE =
+ THREAD_NAME_PATH_PREFIX.size() +
+ IntegerToString<int>::BUFSIZE + // Size of tid
+ 1 + // For '/' character
+ 5; // For the file name "comm" and the nullterminator.
+
+static void construct_thread_name_file_path(cpp::StringStream &stream,
+ int tid) {
+ stream << THREAD_NAME_PATH_PREFIX << tid << '/' << cpp::StringView("comm")
+ << cpp::StringStream::ENDS;
+}
+
+int Thread::set_name(const cpp::StringView &name) {
+ if (name.size() >= NAME_SIZE_MAX)
+ return ERANGE;
+
+ if (*this == self) {
+ // If we are setting the name of the current thread, then we can
+ // use the syscall to set the name.
+ int retval = __llvm_libc::syscall(SYS_prctl, PR_SET_NAME, name.data());
+ if (retval < 0)
+ return -retval;
+ else
+ return 0;
+ }
+
+ char path_name_buffer[THREAD_NAME_PATH_SIZE];
+ cpp::StringStream path_stream(path_name_buffer);
+ construct_thread_name_file_path(path_stream, attrib->tid);
+#ifdef SYS_open
+ int fd = __llvm_libc::syscall(SYS_open, path_name_buffer, O_RDWR);
+#else
+ int fd = __llvm_libc::syscall(SYS_openat, AT_FDCWD, path_name_buffer, O_RDWR);
+#endif
+ if (fd < 0)
+ return -fd;
+
+ int retval = __llvm_libc::syscall(SYS_write, fd, name.data(), name.size());
+ __llvm_libc::syscall(SYS_close, fd);
+
+ if (retval < 0)
+ return -retval;
+ else if (retval != int(name.size()))
+ return EIO;
+ else
+ return 0;
+}
+
+int Thread::get_name(cpp::StringStream &name) const {
+ if (name.bufsize() < NAME_SIZE_MAX)
+ return ERANGE;
+
+ char name_buffer[NAME_SIZE_MAX];
+
+ if (*this == self) {
+ // If we are getting the name of the current thread, then we can
+ // use the syscall to get the name.
+ int retval = __llvm_libc::syscall(SYS_prctl, PR_GET_NAME, name_buffer);
+ if (retval < 0)
+ return -retval;
+ name << name_buffer;
+ return 0;
+ }
+
+ char path_name_buffer[THREAD_NAME_PATH_SIZE];
+ cpp::StringStream path_stream(path_name_buffer);
+ construct_thread_name_file_path(path_stream, attrib->tid);
+#ifdef SYS_open
+ int fd = __llvm_libc::syscall(SYS_open, path_name_buffer, O_RDONLY);
+#else
+ int fd =
+ __llvm_libc::syscall(SYS_openat, AT_FDCWD, path_name_buffer, O_RDONLY);
+#endif
+ if (fd < 0)
+ return -fd;
+
+ int retval = __llvm_libc::syscall(SYS_read, fd, name_buffer, NAME_SIZE_MAX);
+ __llvm_libc::syscall(SYS_close, fd);
+ if (retval < 0)
+ return -retval;
+ if (retval == NAME_SIZE_MAX)
+ return ERANGE;
+ if (name_buffer[retval - 1] == '\n')
+ name_buffer[retval - 1] = '\0';
+ else
+ name_buffer[retval] = '\0';
+ name << name_buffer;
+ return 0;
+}
+
} // namespace __llvm_libc
diff --git a/libc/src/__support/threads/thread.h b/libc/src/__support/threads/thread.h
index 2ca17aa08dcfc..088b967ee4b0b 100644
--- a/libc/src/__support/threads/thread.h
+++ b/libc/src/__support/threads/thread.h
@@ -9,7 +9,9 @@
#ifndef LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
#define LLVM_LIBC_SRC_SUPPORT_THREADS_THREAD_H
+#include "src/__support/CPP/StringView.h"
#include "src/__support/CPP/atomic.h"
+#include "src/__support/CPP/stringstream.h"
#include "src/__support/architectures.h"
#include <stddef.h> // For size_t
@@ -165,6 +167,12 @@ struct Thread {
// Return true if this thread is equal to the other thread.
bool operator==(const Thread &other) const;
+
+ // Set the name of the thread. Return the error number on error.
+ int set_name(const cpp::StringView &name);
+
+ // Return the name of the thread in |name|. Return the error number of error.
+ int get_name(cpp::StringStream &name) const;
};
extern thread_local Thread self;
diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index 4c45cc53a3e82..7d8a72197214e 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -301,3 +301,29 @@ add_entrypoint_object(
libc.include.pthread
libc.src.__support.threads.thread
)
+
+add_entrypoint_object(
+ pthread_setname_np
+ SRCS
+ pthread_setname_np.cpp
+ HDRS
+ pthread_setname_np.h
+ DEPENDS
+ libc.include.pthread
+ libc.src.__support.CPP.array_ref
+ libc.src.__support.CPP.string_view
+ libc.src.__support.threads.thread
+)
+
+add_entrypoint_object(
+ pthread_getname_np
+ SRCS
+ pthread_getname_np.cpp
+ HDRS
+ pthread_getname_np.h
+ DEPENDS
+ libc.include.pthread
+ libc.src.__support.CPP.array_ref
+ libc.src.__support.CPP.stringstream
+ libc.src.__support.threads.thread
+)
diff --git a/libc/src/pthread/pthread_getname_np.cpp b/libc/src/pthread/pthread_getname_np.cpp
new file mode 100644
index 0000000000000..b4d5387482547
--- /dev/null
+++ b/libc/src/pthread/pthread_getname_np.cpp
@@ -0,0 +1,32 @@
+//===-- Linux implementation of the pthread_setname_np function -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "pthread_getname_np.h"
+
+#include "src/__support/CPP/ArrayRef.h"
+#include "src/__support/CPP/StringView.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <pthread.h>
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread),
+ "Mismatch between pthread_t and internal Thread.");
+
+LLVM_LIBC_FUNCTION(int, pthread_getname_np,
+ (pthread_t th, char *buf, size_t len)) {
+ auto *thread = reinterpret_cast<__llvm_libc::Thread *>(&th);
+ cpp::MutableArrayRef<char> name_buf(buf, len);
+ cpp::StringStream name_stream(name_buf);
+ return thread->get_name(name_stream);
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/pthread/pthread_getname_np.h b/libc/src/pthread/pthread_getname_np.h
new file mode 100644
index 0000000000000..da9c77150e0af
--- /dev/null
+++ b/libc/src/pthread/pthread_getname_np.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for pthread_getname_np function ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETNAME_NP_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETNAME_NP_H
+
+#include <pthread.h>
+#include <stddef.h>
+
+namespace __llvm_libc {
+
+int pthread_getname_np(pthread_t, char *, size_t);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_GETNAME_NP_H
diff --git a/libc/src/pthread/pthread_setname_np.cpp b/libc/src/pthread/pthread_setname_np.cpp
new file mode 100644
index 0000000000000..402cb88ab8b80
--- /dev/null
+++ b/libc/src/pthread/pthread_setname_np.cpp
@@ -0,0 +1,28 @@
+//===-- Linux implementation of the pthread_setname_np function -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "pthread_setname_np.h"
+
+#include "src/__support/CPP/StringView.h"
+#include "src/__support/CPP/error.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/thread.h"
+
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+static_assert(sizeof(pthread_t) == sizeof(__llvm_libc::Thread),
+ "Mismatch between pthread_t and internal Thread.");
+
+LLVM_LIBC_FUNCTION(int, pthread_setname_np, (pthread_t th, const char *name)) {
+ auto *thread = reinterpret_cast<__llvm_libc::Thread *>(&th);
+ return thread->set_name(cpp::StringView(name));
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/pthread/pthread_setname_np.h b/libc/src/pthread/pthread_setname_np.h
new file mode 100644
index 0000000000000..25b8c1f4f5e0f
--- /dev/null
+++ b/libc/src/pthread/pthread_setname_np.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pthread_setname_np function ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETNAME_NP_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETNAME_NP_H
+
+#include <pthread.h>
+
+namespace __llvm_libc {
+
+int pthread_setname_np(pthread_t, const char *name);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SETNAME_NP_H
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index e751318bb0012..09219227f3abc 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -53,3 +53,26 @@ add_integration_test(
libc.src.pthread.pthread_join
libc.src.pthread.pthread_self
)
+
+add_integration_test(
+ pthread_name_test
+ SUITE
+ libc-pthread-integration-tests
+ SRCS
+ pthread_name_test.cpp
+ LOADER
+ libc.loader.linux.crt1
+ DEPENDS
+ libc.include.errno
+ libc.include.pthread
+ libc.src.errno.errno
+ libc.src.pthread.pthread_create
+ libc.src.pthread.pthread_getname_np
+ libc.src.pthread.pthread_join
+ libc.src.pthread.pthread_mutex_destroy
+ libc.src.pthread.pthread_mutex_init
+ libc.src.pthread.pthread_mutex_lock
+ libc.src.pthread.pthread_mutex_unlock
+ libc.src.pthread.pthread_self
+ libc.src.pthread.pthread_setname_np
+)
diff --git a/libc/test/integration/src/pthread/pthread_name_test.cpp b/libc/test/integration/src/pthread/pthread_name_test.cpp
new file mode 100644
index 0000000000000..85fdd32d4896b
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_name_test.cpp
@@ -0,0 +1,83 @@
+//===-- Tests for pthread_equal -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/StringView.h"
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_getname_np.h"
+#include "src/pthread/pthread_join.h"
+#include "src/pthread/pthread_mutex_destroy.h"
+#include "src/pthread/pthread_mutex_init.h"
+#include "src/pthread/pthread_mutex_lock.h"
+#include "src/pthread/pthread_mutex_unlock.h"
+#include "src/pthread/pthread_self.h"
+#include "src/pthread/pthread_setname_np.h"
+
+#include "utils/IntegrationTest/test.h"
+
+#include <errno.h>
+#include <pthread.h>
+
+using StringView = __llvm_libc::cpp::StringView;
+
+char child_thread_name_buffer[16];
+pthread_mutex_t mutex;
+
+static void *child_func(void *) {
+ __llvm_libc::pthread_mutex_lock(&mutex);
+ auto self = __llvm_libc::pthread_self();
+ __llvm_libc::pthread_getname_np(self, child_thread_name_buffer, 16);
+ __llvm_libc::pthread_mutex_unlock(&mutex);
+ return nullptr;
+}
+
+TEST_MAIN() {
+ // We init and lock the mutex so that we guarantee that the child thread is
+ // waiting after startup.
+ ASSERT_EQ(__llvm_libc::pthread_mutex_init(&mutex, nullptr), 0);
+ ASSERT_EQ(__llvm_libc::pthread_mutex_lock(&mutex), 0);
+
+ auto main_thread = __llvm_libc::pthread_self();
+ const char MAIN_THREAD_NAME[] = "main_thread";
+ char thread_name_buffer[16];
+ ASSERT_EQ(__llvm_libc::pthread_setname_np(main_thread, MAIN_THREAD_NAME), 0);
+ ASSERT_EQ(
+ __llvm_libc::pthread_getname_np(main_thread, thread_name_buffer, 16), 0);
+ ASSERT_TRUE(StringView(MAIN_THREAD_NAME)
+ .equals(StringView(
+ reinterpret_cast<const char *>(thread_name_buffer))));
+
+ pthread_t th;
+ ASSERT_EQ(__llvm_libc::pthread_create(&th, nullptr, child_func, nullptr), 0);
+ // This new thread should of course not be equal to the main thread.
+ const char CHILD_THREAD_NAME[] = "child_thread";
+ ASSERT_EQ(__llvm_libc::pthread_setname_np(th, CHILD_THREAD_NAME), 0);
+ ASSERT_EQ(__llvm_libc::pthread_getname_np(th, thread_name_buffer, 16), 0);
+ ASSERT_TRUE(StringView(CHILD_THREAD_NAME)
+ .equals(StringView(
+ reinterpret_cast<const char *>(thread_name_buffer))));
+
+ ASSERT_EQ(__llvm_libc::pthread_mutex_unlock(&mutex), 0);
+
+ void *retval;
+ ASSERT_EQ(__llvm_libc::pthread_join(th, &retval), 0);
+ ASSERT_EQ(uintptr_t(retval), uintptr_t(nullptr));
+ // Make sure that the child thread saw it name correctly.
+ ASSERT_TRUE(StringView(CHILD_THREAD_NAME)
+ .equals(StringView(reinterpret_cast<const char *>(
+ child_thread_name_buffer))));
+
+ __llvm_libc::pthread_mutex_destroy(&mutex);
+
+ ASSERT_EQ(__llvm_libc::pthread_setname_np(main_thread,
+ "a really long name for a thread"),
+ ERANGE);
+ char smallbuf[1];
+ ASSERT_EQ(__llvm_libc::pthread_getname_np(main_thread, smallbuf, 1), ERANGE);
+
+ return 0;
+}
More information about the libc-commits
mailing list