[libc++] Add tests to make sure that stable algorithms work without memory available
authorNikolas Klauser <nikolasklauser@berlin.de>
Thu, 15 Jun 2023 15:40:42 +0000 (08:40 -0700)
committerNikolas Klauser <nikolasklauser@berlin.de>
Thu, 15 Jun 2023 17:59:34 +0000 (10:59 -0700)
Reviewed By: #libc, ldionne

Spies: power-llvm-team, ldionne, libcxx-commits, arichardson, mstorsjo

Differential Revision: https://reviews.llvm.org/D152939

libcxx/include/__algorithm/stable_partition.h
libcxx/test/std/algorithms/alg.modifying.operations/alg.partitions/stable_partition.pass.cpp
libcxx/test/std/algorithms/alg.sorting/alg.merge/inplace_merge.pass.cpp
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
libcxx/test/support/count_new.h

index 3b68bd3..38fa9ce 100644 (file)
@@ -122,7 +122,10 @@ _LIBCPP_HIDE_FROM_ABI _ForwardIterator
 __stable_partition_impl(_ForwardIterator __first, _ForwardIterator __last, _Predicate __pred,
                    forward_iterator_tag)
 {
-    const unsigned __alloc_limit = 3;  // might want to make this a function of trivial assignment
+    typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type;
+    typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
+
+    const difference_type __alloc_limit = 3;  // might want to make this a function of trivial assignment
     // Either prove all true and return __first or point to first false
     while (true)
     {
@@ -134,8 +137,6 @@ __stable_partition_impl(_ForwardIterator __first, _ForwardIterator __last, _Pred
     }
     // We now have a reduced range [__first, __last)
     // *__first is known to be false
-    typedef typename iterator_traits<_ForwardIterator>::difference_type difference_type;
-    typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
     difference_type __len = _IterOps<_AlgPolicy>::distance(__first, __last);
     pair<value_type*, ptrdiff_t> __p(0, 0);
     unique_ptr<value_type, __return_temporary_buffer> __h;
index 948ec48..5fc6633 100644 (file)
 #include <algorithm>
 #include <cassert>
 #include <memory>
+#include <vector>
 
-#include "test_macros.h"
+#include "count_new.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct is_odd
 {
@@ -36,7 +38,7 @@ template <class Iter>
 void
 test()
 {
-    {  // check mixed
+  { // check mixed
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -64,8 +66,8 @@ test()
     assert(array[7] == P(2, 2));
     assert(array[8] == P(4, 1));
     assert(array[9] == P(4, 2));
-    }
-    {
+  }
+  {
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -104,8 +106,8 @@ test()
     r = std::stable_partition(Iter(array+4), Iter(array+5), odd_first());
     assert(base(r) == array+4);
     assert(array[4] == P(0, 1));
-    }
-    {  // check all false
+  }
+  { // check all false
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -133,8 +135,8 @@ test()
     assert(array[7] == P(6, 2));
     assert(array[8] == P(8, 1));
     assert(array[9] == P(8, 2));
-    }
-    {  // check all true
+  }
+  { // check all true
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -162,8 +164,8 @@ test()
     assert(array[7] == P(7, 2));
     assert(array[8] == P(9, 1));
     assert(array[9] == P(9, 2));
-    }
-    {  // check all false but first true
+  }
+  { // check all false but first true
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -191,8 +193,8 @@ test()
     assert(array[7] == P(6, 2));
     assert(array[8] == P(8, 1));
     assert(array[9] == P(8, 2));
-    }
-    {  // check all false but last true
+  }
+  { // check all false but last true
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -220,8 +222,8 @@ test()
     assert(array[7] == P(6, 1));
     assert(array[8] == P(6, 2));
     assert(array[9] == P(8, 1));
-    }
-    {  // check all true but first false
+  }
+  { // check all true but first false
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -249,8 +251,8 @@ test()
     assert(array[7] == P(9, 1));
     assert(array[8] == P(9, 2));
     assert(array[9] == P(0, 1));
-    }
-    {  // check all true but last false
+  }
+  { // check all true but last false
     typedef std::pair<int,int> P;
     P array[] =
     {
@@ -278,7 +280,24 @@ test()
     assert(array[7] == P(7, 2));
     assert(array[8] == P(9, 1));
     assert(array[9] == P(0, 2));
-    }
+  }
+#if TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
+  { // check that the algorithm still works when no memory is available
+    std::vector<int> vec(150, 3);
+    vec[5]                             = 6;
+    getGlobalMemCounter()->throw_after = 0;
+    std::stable_partition(vec.begin(), vec.end(), [](int i) { return i < 5; });
+    assert(std::is_partitioned(vec.begin(), vec.end(), [](int i) { return i < 5; }));
+    vec[5]                             = 6;
+    getGlobalMemCounter()->throw_after = 0;
+    std::stable_partition(
+        forward_iterator<int*>(vec.data()), forward_iterator<int*>(vec.data() + vec.size()), [](int i) {
+          return i < 5;
+        });
+    assert(std::is_partitioned(vec.begin(), vec.end(), [](int i) { return i < 5; }));
+    getGlobalMemCounter()->reset();
+  }
+#endif // TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
 }
 
 #if TEST_STD_VER >= 11
index 1271865..44f2868 100644 (file)
 //   void
 //   inplace_merge(Iter first, Iter middle, Iter last);
 
+// XFAIL: LIBCXX-AIX-FIXME
+
 #include <algorithm>
-#include <random>
 #include <cassert>
+#include <random>
+#include <vector>
 
-#include "test_macros.h"
+#include "count_new.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 #if TEST_STD_VER >= 11
 struct S {
@@ -107,7 +111,16 @@ int main(int, char**)
     test<bidirectional_iterator<S*> >();
     test<random_access_iterator<S*> >();
     test<S*>();
-#endif // TEST_STD_VER >= 11
+#endif
+
+#if TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
+    {
+        std::vector<int> vec(150, 3);
+        getGlobalMemCounter()->throw_after = 0;
+        std::inplace_merge(vec.begin(), vec.begin() + 100, vec.end());
+        assert(std::all_of(vec.begin(), vec.end(), [](int i) { return i == 3; }));
+    }
+#endif // TEST_STD_VER >= 11 && !defined(TEST_HAS_NO_EXCEPTIONS)
 
   return 0;
 }
index aee94c6..765e50e 100644 (file)
 //   stable_sort(Iter first, Iter last);
 
 #include <algorithm>
+#include <cassert>
 #include <iterator>
 #include <random>
-#include <cassert>
 
+#include "count_new.h"
 #include "test_macros.h"
 
 std::mt19937 randomness;
@@ -155,5 +156,13 @@ int main(int, char**)
     test_larger_sorts(1000);
     test_larger_sorts(1009);
 
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+    { // check that the algorithm works without memory
+        std::vector<int> vec(150, 3);
+        getGlobalMemCounter()->throw_after = 0;
+        std::stable_sort(vec.begin(), vec.end());
+    }
+#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
+
   return 0;
 }
index fc53cd6..3e3856c 100644 (file)
@@ -9,9 +9,10 @@
 #ifndef COUNT_NEW_H
 #define COUNT_NEW_H
 
-# include <cstdlib>
-# include <cassert>
-# include <new>
+#include <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <new>
 #include <type_traits>
 
 #include "test_macros.h"
@@ -78,7 +79,6 @@ public:
     void newCalled(std::size_t s)
     {
         assert(disable_allocations == false);
-        assert(s);
         if (throw_after == 0) {
             throw_after = never_throw_value;
             detail::throw_bad_alloc_helper();
@@ -112,7 +112,6 @@ public:
     void newArrayCalled(std::size_t s)
     {
         assert(disable_allocations == false);
-        assert(s);
         if (throw_after == 0) {
             throw_after = never_throw_value;
             detail::throw_bad_alloc_helper();
@@ -410,11 +409,11 @@ void operator delete[](void* p) TEST_NOEXCEPT
 void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   const std::size_t a = static_cast<std::size_t>(av);
   getGlobalMemCounter()->alignedNewCalled(s, a);
-  void *ret;
+  void *ret = nullptr;
 #ifdef USE_ALIGNED_ALLOC
   ret = _aligned_malloc(s, a);
 #else
-  posix_memalign(&ret, a, s);
+  assert(posix_memalign(&ret, std::max(a, sizeof(void*)), s) != EINVAL);
 #endif
   if (ret == nullptr)
     detail::throw_bad_alloc_helper();