Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++][NFC] Refactor the core logic of operator new into helper functions #69407

Merged
merged 1 commit into from
Oct 18, 2023

Conversation

ldionne
Copy link
Member

@ldionne ldionne commented Oct 18, 2023

This will make it easier to implement new(nothrow) without calling the throwing version of new when exceptions are disabled. See https://llvm.org/D150610 for the full discussion.

@ldionne ldionne requested review from a team as code owners October 18, 2023 02:15
@llvmbot llvmbot added libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. libc++abi libc++abi C++ Runtime Library. Not libc++. labels Oct 18, 2023
@llvmbot
Copy link
Member

llvmbot commented Oct 18, 2023

@llvm/pr-subscribers-libcxx

@llvm/pr-subscribers-libcxxabi

Author: Louis Dionne (ldionne)

Changes

This will make it easier to implement new(nothrow) without calling the throwing version of new when exceptions are disabled. See https://llvm.org/D150610 for the full discussion.


Full diff: https://github.com/llvm/llvm-project/pull/69407.diff

2 Files Affected:

  • (modified) libcxx/src/new.cpp (+29-25)
  • (modified) libcxxabi/src/stdlib_new_delete.cpp (+29-25)
diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp
index c435c5ffc809c61..d045b0d4570d096 100644
--- a/libcxx/src/new.cpp
+++ b/libcxx/src/new.cpp
@@ -20,30 +20,34 @@
 // in this shared library, so that they can be overridden by programs
 // that define non-weak copies of the functions.
 
-_LIBCPP_WEAK
-void *
-operator new(std::size_t size) _THROW_BAD_ALLOC
-{
+static void* operator_new_impl(std::size_t size) noexcept {
     if (size == 0)
         size = 1;
     void* p;
-    while ((p = std::malloc(size)) == nullptr)
-    {
+    while ((p = std::malloc(size)) == nullptr) {
         // If malloc fails and there is a new_handler,
         // call it to try free up memory.
         std::new_handler nh = std::get_new_handler();
         if (nh)
             nh();
         else
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
-            throw std::bad_alloc();
-#else
             break;
-#endif
     }
     return p;
 }
 
+_LIBCPP_WEAK
+void *
+operator new(std::size_t size) _THROW_BAD_ALLOC
+{
+    void* p = operator_new_impl(size);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (p == nullptr)
+        throw std::bad_alloc();
+#endif
+    return p;
+}
+
 _LIBCPP_WEAK
 void*
 operator new(size_t size, const std::nothrow_t&) noexcept
@@ -133,10 +137,7 @@ operator delete[] (void* ptr, size_t) noexcept
 
 #if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION)
 
-_LIBCPP_WEAK
-void *
-operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC
-{
+static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) noexcept {
     if (size == 0)
         size = 1;
     if (static_cast<size_t>(alignment) < sizeof(void*))
@@ -145,26 +146,29 @@ operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC
     // Try allocating memory. If allocation fails and there is a new_handler,
     // call it to try free up memory, and try again until it succeeds, or until
     // the new_handler decides to terminate.
-    //
-    // If allocation fails and there is no new_handler, we throw bad_alloc
-    // (or return nullptr if exceptions are disabled).
     void* p;
-    while ((p = std::__libcpp_aligned_alloc(static_cast<std::size_t>(alignment), size)) == nullptr)
-    {
+    while ((p = std::__libcpp_aligned_alloc(static_cast<std::size_t>(alignment), size)) == nullptr) {
         std::new_handler nh = std::get_new_handler();
         if (nh)
             nh();
-        else {
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
-            throw std::bad_alloc();
-#else
+        else
             break;
-#endif
-        }
     }
     return p;
 }
 
+_LIBCPP_WEAK
+void *
+operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC
+{
+    void* p = operator_new_aligned_impl(size, alignment);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (p == nullptr)
+        throw std::bad_alloc();
+#endif
+    return p;
+}
+
 _LIBCPP_WEAK
 void*
 operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept
diff --git a/libcxxabi/src/stdlib_new_delete.cpp b/libcxxabi/src/stdlib_new_delete.cpp
index 080f932ccc60ecf..7946d07a59b72a7 100644
--- a/libcxxabi/src/stdlib_new_delete.cpp
+++ b/libcxxabi/src/stdlib_new_delete.cpp
@@ -30,30 +30,34 @@
 // in this shared library, so that they can be overridden by programs
 // that define non-weak copies of the functions.
 
-_LIBCPP_WEAK
-void *
-operator new(std::size_t size) _THROW_BAD_ALLOC
-{
+static void* operator_new_impl(std::size_t size) noexcept {
     if (size == 0)
         size = 1;
     void* p;
-    while ((p = std::malloc(size)) == nullptr)
-    {
+    while ((p = std::malloc(size)) == nullptr) {
         // If malloc fails and there is a new_handler,
         // call it to try free up memory.
         std::new_handler nh = std::get_new_handler();
         if (nh)
             nh();
         else
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
-            throw std::bad_alloc();
-#else
             break;
-#endif
     }
     return p;
 }
 
+_LIBCPP_WEAK
+void *
+operator new(std::size_t size) _THROW_BAD_ALLOC
+{
+    void* p = operator_new_impl(size);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (p == nullptr)
+        throw std::bad_alloc();
+#endif
+    return p;
+}
+
 _LIBCPP_WEAK
 void*
 operator new(size_t size, const std::nothrow_t&) noexcept
@@ -143,10 +147,7 @@ operator delete[] (void* ptr, size_t) noexcept
 
 #if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION)
 
-_LIBCPP_WEAK
-void *
-operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC
-{
+static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignment) noexcept {
     if (size == 0)
         size = 1;
     if (static_cast<size_t>(alignment) < sizeof(void*))
@@ -155,26 +156,29 @@ operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC
     // Try allocating memory. If allocation fails and there is a new_handler,
     // call it to try free up memory, and try again until it succeeds, or until
     // the new_handler decides to terminate.
-    //
-    // If allocation fails and there is no new_handler, we throw bad_alloc
-    // (or return nullptr if exceptions are disabled).
     void* p;
-    while ((p = std::__libcpp_aligned_alloc(static_cast<std::size_t>(alignment), size)) == nullptr)
-    {
+    while ((p = std::__libcpp_aligned_alloc(static_cast<std::size_t>(alignment), size)) == nullptr) {
         std::new_handler nh = std::get_new_handler();
         if (nh)
             nh();
-        else {
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
-            throw std::bad_alloc();
-#else
+        else
             break;
-#endif
-        }
     }
     return p;
 }
 
+_LIBCPP_WEAK
+void *
+operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC
+{
+    void* p = operator_new_aligned_impl(size, alignment);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (p == nullptr)
+        throw std::bad_alloc();
+#endif
+    return p;
+}
+
 _LIBCPP_WEAK
 void*
 operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept

@github-actions
Copy link

github-actions bot commented Oct 18, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

libcxx/src/new.cpp Outdated Show resolved Hide resolved
…ctions

This will make it easier to implement new(nothrow) without calling
the throwing version of new when exceptions are disabled. See
https://llvm.org/D150610 for the full discussion.
@ldionne ldionne force-pushed the review/refactor-operator-new branch from 57827e7 to fc787ff Compare October 18, 2023 18:26
@ldionne ldionne merged commit e494a96 into llvm:main Oct 18, 2023
3 checks passed
@ldionne ldionne deleted the review/refactor-operator-new branch October 18, 2023 18:33
@@ -20,7 +20,7 @@
// in this shared library, so that they can be overridden by programs
// that define non-weak copies of the functions.

_LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC {
static void* operator_new_impl(std::size_t size) noexcept {
Copy link
Contributor

@azhan92 azhan92 Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the standard allows the following behaviour for the new_handler function:

throw an exception of type bad_alloc or a class derived from bad_alloc;

nh() may throw an exception and cause some issues with the noexcept specification on operator_new_impl. Would you consider a change here to prevent problem?

Copy link
Member Author

@ldionne ldionne Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think this looks like an issue I introduced with this patch. It should be simple to fix, basically make the _impl function non-noexcept and add a test that catches that? A patch would definitely be appreciated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll create a patch to do that then. Thanks for the suggestions

ldionne pushed a commit that referenced this pull request Jan 22, 2024
)

This patch removes the noexcept specifier introduced in #69407 since the
Standard allows a new handler to throw an exception of type bad_alloc 
(or derived from it). With the noexcept specifier on the helper
functions, we would immediately terminate the program.

The patch also adds tests for the case that had regressed.

Co-authored-by: Alison Zhang <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++abi libc++abi C++ Runtime Library. Not libc++. libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants