From 377f30c00f3b8b8b0da748dbb9d988fa67a8ec2f Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Tue, 20 Sep 2016 18:15:36 +0300 Subject: [PATCH] re PR libstdc++/77619 (uninitialized_meow_construct and friends not exception safe) PR libstdc++/77619 * include/bits/stl_construct.h: (_Construct_novalue): New. (_Destroy_n_aux, _Destroy_n): New. * include/bits/stl_uninitialized.h: (type_traits): New include in C++11 mode. (__uninitialized_default_novalue_1): New. (__uninitialized_default_novalue_n_1): Likewise. (__uninitialized_default_novalue): Likewise. (__uninitialized_default_novalue_n): Likewise. (__uninitialized_copy_n_pair): Likewise. (uninitialized_default_construct): Use __uninitialized_default_novalue. (uninitialized_default_construct_n): Use __uninitialized_default_novalue_n. (uninitialized_value_construct): Use __uninitialized_default. (uninitialized_value_construct_n): Use __uninitialized_default_n. (uninitialized_move): Use uninitialized_copy. (uninitialized_move_n): Use __uninitialized_copy_n_pair. (destroy_at): Use _Destroy. (destroy): Likewise. (destroy_n): Likewise. * testsuite/20_util/specialized_algorithms/ memory_management_tools/1.cc: Add tests for exceptions, add tests for trivial cases for construct and move. From-SVN: r240264 --- libstdc++-v3/ChangeLog | 27 +++ libstdc++-v3/include/bits/stl_construct.h | 45 +++++ libstdc++-v3/include/bits/stl_uninitialized.h | 185 +++++++++++++++--- .../memory_management_tools/1.cc | 183 +++++++++++++++++ 4 files changed, 410 insertions(+), 30 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index b50f6296982..3981509f5d9 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,30 @@ +2016-09-20 Ville Voutilainen + + PR libstdc++/77619 + * include/bits/stl_construct.h: (_Construct_novalue): New. + (_Destroy_n_aux, _Destroy_n): New. + * include/bits/stl_uninitialized.h: (type_traits): + New include in C++11 mode. + (__uninitialized_default_novalue_1): New. + (__uninitialized_default_novalue_n_1): Likewise. + (__uninitialized_default_novalue): Likewise. + (__uninitialized_default_novalue_n): Likewise. + (__uninitialized_copy_n_pair): Likewise. + (uninitialized_default_construct): + Use __uninitialized_default_novalue. + (uninitialized_default_construct_n): + Use __uninitialized_default_novalue_n. + (uninitialized_value_construct): Use __uninitialized_default. + (uninitialized_value_construct_n): Use __uninitialized_default_n. + (uninitialized_move): Use uninitialized_copy. + (uninitialized_move_n): Use __uninitialized_copy_n_pair. + (destroy_at): Use _Destroy. + (destroy): Likewise. + (destroy_n): Likewise. + * testsuite/20_util/specialized_algorithms/ + memory_management_tools/1.cc: Add tests for exceptions, + add tests for trivial cases for construct and move. + 2016-09-20 Jonathan Wakely * python/libstdcxx/v6/xmethods.py (DequeWorkerBase.__init__) diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h index 3d126288d85..4a771c4f0d1 100644 --- a/libstdc++-v3/include/bits/stl_construct.h +++ b/libstdc++-v3/include/bits/stl_construct.h @@ -84,6 +84,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif + template + inline void + _Construct_novalue(_T1* __p) + { ::new(static_cast(__p)) _T1; } + /** * Destroy the object pointed to by a pointer type. */ @@ -127,6 +132,46 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __destroy(__first, __last); } + template + struct _Destroy_n_aux + { + template + static _ForwardIterator + __destroy_n(_ForwardIterator __first, _Size __count) + { + for (; __count > 0; (void)++__first, --__count) + std::_Destroy(std::__addressof(*__first)); + return __first; + } + }; + + template<> + struct _Destroy_n_aux + { + template + static _ForwardIterator + __destroy_n(_ForwardIterator __first, _Size __count) + { + std::advance(__first, __count); + return __first; + } + }; + + /** + * Destroy a range of objects. If the value_type of the object has + * a trivial destructor, the compiler should optimize all of this + * away, otherwise the objects' destructors must be invoked. + */ + template + inline _ForwardIterator + _Destroy_n(_ForwardIterator __first, _Size __count) + { + typedef typename iterator_traits<_ForwardIterator>::value_type + _Value_type; + return std::_Destroy_n_aux<__has_trivial_destructor(_Value_type)>:: + __destroy_n(__first, __count); + } + /** * Destroy a range of objects using the supplied allocator. For * nondefault allocators we do not optimize away invocation of diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h index c5c81fb2c66..ef2e584c1b3 100644 --- a/libstdc++-v3/include/bits/stl_uninitialized.h +++ b/libstdc++-v3/include/bits/stl_uninitialized.h @@ -60,6 +60,10 @@ #include #endif +#if __cplusplus >= 201103L +#include +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -640,6 +644,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION allocator<_Tp>&) { return std::__uninitialized_default_n(__first, __n); } + template + struct __uninitialized_default_novalue_1 + { + template + static void + __uninit_default_novalue(_ForwardIterator __first, + _ForwardIterator __last) + { + _ForwardIterator __cur = __first; + __try + { + for (; __cur != __last; ++__cur) + std::_Construct_novalue(std::__addressof(*__cur)); + } + __catch(...) + { + std::_Destroy(__first, __cur); + __throw_exception_again; + } + } + }; + + template<> + struct __uninitialized_default_novalue_1 + { + template + static void + __uninit_default_novalue(_ForwardIterator __first, + _ForwardIterator __last) + { + } + }; + + template + struct __uninitialized_default_novalue_n_1 + { + template + static _ForwardIterator + __uninit_default_novalue_n(_ForwardIterator __first, _Size __n) + { + _ForwardIterator __cur = __first; + __try + { + for (; __n > 0; --__n, ++__cur) + std::_Construct_novalue(std::__addressof(*__cur)); + return __cur; + } + __catch(...) + { + std::_Destroy(__first, __cur); + __throw_exception_again; + } + } + }; + + template<> + struct __uninitialized_default_novalue_n_1 + { + template + static _ForwardIterator + __uninit_default_novalue_n(_ForwardIterator __first, _Size __n) + { + } + }; + + // __uninitialized_default_novalue + // Fills [first, last) with std::distance(first, last) default-initialized + // value_types(s). + template + inline void + __uninitialized_default_novalue(_ForwardIterator __first, + _ForwardIterator __last) + { + typedef typename iterator_traits<_ForwardIterator>::value_type + _ValueType; + + std::__uninitialized_default_novalue_1< + is_trivially_default_constructible<_ValueType>::value>:: + __uninit_default_novalue(__first, __last); + } + + // __uninitialized_default_n + // Fills [first, first + n) with n default-initialized value_type(s). + template + inline _ForwardIterator + __uninitialized_default_novalue_n(_ForwardIterator __first, _Size __n) + { + typedef typename iterator_traits<_ForwardIterator>::value_type + _ValueType; + + return __uninitialized_default_novalue_n_1< + is_trivially_default_constructible<_ValueType>::value>:: + __uninit_default_novalue_n(__first, __n); + } template @@ -669,6 +767,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION random_access_iterator_tag) { return std::uninitialized_copy(__first, __first + __n, __result); } + template + pair<_InputIterator, _ForwardIterator> + __uninitialized_copy_n_pair(_InputIterator __first, _Size __n, + _ForwardIterator __result, input_iterator_tag) + { + _ForwardIterator __cur = __result; + __try + { + for (; __n > 0; --__n, ++__first, ++__cur) + std::_Construct(std::__addressof(*__cur), *__first); + return {__first, __cur}; + } + __catch(...) + { + std::_Destroy(__result, __cur); + __throw_exception_again; + } + } + + template + inline pair<_RandomAccessIterator, _ForwardIterator> + __uninitialized_copy_n_pair(_RandomAccessIterator __first, _Size __n, + _ForwardIterator __result, + random_access_iterator_tag) + { + auto __second_res = uninitialized_copy(__first, __first + __n, __result); + auto __first_res = std::next(__first, __n); + return {__first_res, __second_res}; + } + /** * @brief Copies the range [first,first+n) into result. * @param __first An input iterator. @@ -684,6 +814,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _ForwardIterator __result) { return std::__uninitialized_copy_n(__first, __n, __result, std::__iterator_category(__first)); } + + template + inline pair<_InputIterator, _ForwardIterator> + __uninitialized_copy_n_pair(_InputIterator __first, _Size __n, + _ForwardIterator __result) + { + return + std::__uninitialized_copy_n_pair(__first, __n, __result, + std::__iterator_category(__first)); + } + #endif #if __cplusplus > 201402L @@ -692,19 +833,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) { - for (; __first != __last; ++__first) - ::new (static_cast(std::__addressof(*__first))) - typename iterator_traits<_ForwardIterator>::value_type; + __uninitialized_default_novalue(__first, __last); } template inline _ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __count) { - for (; __count > 0; (void)++__first, --__count) - ::new (static_cast(std::__addressof(*__first))) - typename iterator_traits<_ForwardIterator>::value_type; - return __first; + return __uninitialized_default_novalue_n(__first, __count); } template @@ -712,19 +848,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION uninitialized_value_construct(_ForwardIterator __first, _ForwardIterator __last) { - for (; __first != __last; ++__first) - ::new (static_cast(std::__addressof(*__first))) - typename iterator_traits<_ForwardIterator>::value_type(); + return __uninitialized_default(__first, __last); } template inline _ForwardIterator uninitialized_value_construct_n(_ForwardIterator __first, _Size __count) { - for (; __count > 0; (void)++__first, --__count) - ::new (static_cast(std::__addressof(*__first))) - typename iterator_traits<_ForwardIterator>::value_type(); - return __first; + return __uninitialized_default_n(__first, __count); } template @@ -732,11 +863,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION uninitialized_move(_InputIterator __first, _InputIterator __last, _ForwardIterator __result) { - for (; __first != __last; (void)++__result, ++__first) - ::new (static_cast(std::__addressof(*__result))) - typename - iterator_traits<_ForwardIterator>::value_type(std::move(*__first)); - return __result; + return std::uninitialized_copy + (_GLIBCXX_MAKE_MOVE_ITERATOR(__first), + _GLIBCXX_MAKE_MOVE_ITERATOR(__last), __result); } template @@ -744,35 +873,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION uninitialized_move_n(_InputIterator __first, _Size __count, _ForwardIterator __result) { - for (; __count > 0; ++__result, (void) ++__first, --__count) - ::new (static_cast(std::__addressof(*__result))) - typename - iterator_traits<_ForwardIterator>::value_type(std::move(*__first)); - return {__first, __result}; + auto __res = std::__uninitialized_copy_n_pair + (_GLIBCXX_MAKE_MOVE_ITERATOR(__first), + __count, __result); + return {__res.first.base(), __res.second}; } template inline void destroy_at(_Tp* __location) { - __location->~_Tp(); + std::_Destroy(__location); } template inline void destroy(_ForwardIterator __first, _ForwardIterator __last) { - for (; __first != __last; ++__first) - std::destroy_at(std::__addressof(*__first)); + std::_Destroy(__first, __last); } template inline _ForwardIterator destroy_n(_ForwardIterator __first, _Size __count) { - for (; __count > 0; (void)++__first, --__count) - std::destroy_at(std::__addressof(*__first)); - return __first; + return std::_Destroy_n(__first, __count); } #endif diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/1.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/1.cc index ec72b826a53..84a68579344 100644 --- a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/1.cc +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/1.cc @@ -21,14 +21,40 @@ #include #include #include +#include int del_count = 0; +int ctor_count = 0; +int throw_after = 0; struct DelCount { ~DelCount() { ++del_count; } }; +struct ThrowAfterN +{ + ThrowAfterN() + { + if (++ctor_count == throw_after) { + std::ostringstream os; + os << "ThrowAfterN(), ctor_count: " << ctor_count + << " throw_after: " << throw_after << std::endl; + throw std::runtime_error(os.str()); + } + } + ThrowAfterN(ThrowAfterN&&) + { + if (++ctor_count == throw_after) { + std::ostringstream os; + os << "ThrowAfterN(), ctor_count: " << ctor_count + << " throw_after: " << throw_after << std::endl; + throw std::runtime_error(os.str()); + } + } + DelCount dc; +}; + void test01() { char test_data[] = "123456"; @@ -118,6 +144,153 @@ void test09() free(target); } +void test10() +{ + char* x = (char*)malloc(sizeof(char)*10); + for (int i = 0; i < 10; ++i) new (x+i) char; + std::destroy(x, x+10); + free(x); +} + +void test11() +{ + char* x = (char*)malloc(sizeof(char)*10); + for (int i = 0; i < 10; ++i) new (x+i) char; + std::destroy_n(x, 10); + free(x); +} + +void test12() +{ + throw_after = 5; + del_count = 0; + ctor_count = 0; + ThrowAfterN* target = + (ThrowAfterN*)malloc(sizeof(ThrowAfterN)*10); + try { + std::uninitialized_default_construct(target, target+10); + } catch (...) { + } + VERIFY(ctor_count == 5); + VERIFY(del_count == 5); + throw_after = 0; + del_count = 0; + ctor_count = 0; +} + +void test13() +{ + throw_after = 5; + del_count = 0; + ctor_count = 0; + ThrowAfterN* target = + (ThrowAfterN*)malloc(sizeof(ThrowAfterN)*10); + try { + std::uninitialized_value_construct(target, target+10); + } catch (...) { + } + VERIFY(ctor_count == 5); + VERIFY(del_count == 5); + throw_after = 0; + del_count = 0; + ctor_count = 0; +} + +void test14() +{ + throw_after = 5; + del_count = 0; + ctor_count = 0; + ThrowAfterN* target = + (ThrowAfterN*)malloc(sizeof(ThrowAfterN)*10); + try { + std::uninitialized_default_construct_n(target, 10); + } catch (...) { + } + VERIFY(ctor_count == 5); + VERIFY(del_count == 5); + throw_after = 0; + del_count = 0; + ctor_count = 0; +} + +void test15() +{ + throw_after = 5; + del_count = 0; + ctor_count = 0; + ThrowAfterN* target = + (ThrowAfterN*)malloc(sizeof(ThrowAfterN)*10); + try { + std::uninitialized_value_construct_n(target, 10); + } catch (...) { + } + VERIFY(ctor_count == 5); + VERIFY(del_count == 5); + throw_after = 0; + del_count = 0; + ctor_count = 0; +} + +void test16() +{ + std::vector source(10); + del_count = 0; + ctor_count = 0; + throw_after = 5; + throw_after = 5; + ThrowAfterN* target = + (ThrowAfterN*)malloc(sizeof(ThrowAfterN)*10); + try { + std::uninitialized_move(source.begin(), source.end(), target); + } catch (...) { + } + VERIFY(ctor_count == 5); + VERIFY(del_count == 5); + throw_after = 0; + del_count = 0; + ctor_count = 0; +} + +void test17() +{ + std::vector source(10); + del_count = 0; + ctor_count = 0; + throw_after = 5; + ThrowAfterN* target = + (ThrowAfterN*)malloc(sizeof(ThrowAfterN)*10); + try { + std::uninitialized_move_n(source.begin(), 10, target); + } catch (...) { + } + VERIFY(ctor_count == 5); + VERIFY(del_count == 5); + throw_after = 0; + del_count = 0; + ctor_count = 0; +} + +void test18() +{ + char test_source[] = "123456"; + char test_target[] = "000000"; + std::uninitialized_move(std::begin(test_source), + std::end(test_source), + test_target); + VERIFY(std::string(test_target) == "123456"); +} + +void test19() +{ + char test_source[] = "123456"; + char test_target[] = "000000"; + std::uninitialized_move_n(std::begin(test_source), + 6, + test_target); + VERIFY(std::string(test_target) == "123456"); +} + int main() { test01(); @@ -129,4 +302,14 @@ int main() test07(); test08(); test09(); + test10(); + test11(); + test12(); + test13(); + test14(); + test15(); + test16(); + test17(); + test18(); + test19(); } -- 2.30.2