re PR libstdc++/78389 (list::merge and list::sort are not exception safe)

PR libstdc++/78389
* include/bits/list.tcc (merge(list&&)):
Adjust list sizes if the comparator throws.
(merge(list&&, _StrictWeakOrdering)): Likewise.
(sort()): Splice elements back from the scratch buffers
if the comparator throws.
(sort(_StrictWeakOrdering)): Likewise.
* testsuite/23_containers/list/operations/78389.cc: New.

From-SVN: r244439
This commit is contained in:
Ville Voutilainen 2017-01-13 16:46:25 +02:00 committed by Ville Voutilainen
parent 45cac8baeb
commit e5dcfacf43
3 changed files with 198 additions and 63 deletions

View File

@ -1,3 +1,14 @@
2017-01-13 Ville Voutilainen <ville.voutilainen@gmail.com>
PR libstdc++/78389
* include/bits/list.tcc (merge(list&&)):
Adjust list sizes if the comparator throws.
(merge(list&&, _StrictWeakOrdering)): Likewise.
(sort()): Splice elements back from the scratch buffers
if the comparator throws.
(sort(_StrictWeakOrdering)): Likewise.
* testsuite/23_containers/list/operations/78389.cc: New.
2017-01-13 Jonathan Wakely <jwakely@redhat.com>
* testsuite/23_containers/unordered_set/allocator/ext_ptr.cc: Mark

View File

@ -380,26 +380,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
// 300. list::merge() specification incomplete
if (this != std::__addressof(__x))
{
_M_check_equal_allocators(__x);
_M_check_equal_allocators(__x);
iterator __first1 = begin();
iterator __last1 = end();
iterator __first2 = __x.begin();
iterator __last2 = __x.end();
while (__first1 != __last1 && __first2 != __last2)
if (*__first2 < *__first1)
{
iterator __next = __first2;
_M_transfer(__first1, __first2, ++__next);
__first2 = __next;
}
else
++__first1;
if (__first2 != __last2)
_M_transfer(__last1, __first2, __last2);
const size_t __orig_size = __x.size();
__try {
while (__first1 != __last1 && __first2 != __last2)
if (*__first2 < *__first1)
{
iterator __next = __first2;
_M_transfer(__first1, __first2, ++__next);
__first2 = __next;
}
else
++__first1;
if (__first2 != __last2)
_M_transfer(__last1, __first2, __last2);
this->_M_inc_size(__x._M_get_size());
__x._M_set_size(0);
this->_M_inc_size(__x._M_get_size());
__x._M_set_size(0);
}
__catch(...)
{
size_t __dist = std::distance(__first2, __last2);
this->_M_inc_size(__dist);
__x._M_set_size(__orig_size - __dist);
__throw_exception_again;
}
}
}
@ -423,20 +433,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __last1 = end();
iterator __first2 = __x.begin();
iterator __last2 = __x.end();
while (__first1 != __last1 && __first2 != __last2)
if (__comp(*__first2, *__first1))
{
iterator __next = __first2;
_M_transfer(__first1, __first2, ++__next);
__first2 = __next;
}
else
++__first1;
if (__first2 != __last2)
_M_transfer(__last1, __first2, __last2);
const size_t __orig_size = __x.size();
__try
{
while (__first1 != __last1 && __first2 != __last2)
if (__comp(*__first2, *__first1))
{
iterator __next = __first2;
_M_transfer(__first1, __first2, ++__next);
__first2 = __next;
}
else
++__first1;
if (__first2 != __last2)
_M_transfer(__last1, __first2, __last2);
this->_M_inc_size(__x._M_get_size());
__x._M_set_size(0);
this->_M_inc_size(__x._M_get_size());
__x._M_set_size(0);
}
__catch(...)
{
size_t __dist = std::distance(__first2, __last2);
this->_M_inc_size(__dist);
__x._M_set_size(__orig_size - __dist);
__throw_exception_again;
}
}
}
@ -453,27 +474,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
list __tmp[64];
list * __fill = __tmp;
list * __counter;
do
__try
{
__carry.splice(__carry.begin(), *this, begin());
for(__counter = __tmp;
__counter != __fill && !__counter->empty();
++__counter)
do
{
__counter->merge(__carry);
__carry.swap(*__counter);
}
__carry.swap(*__counter);
if (__counter == __fill)
++__fill;
}
while ( !empty() );
__carry.splice(__carry.begin(), *this, begin());
for (__counter = __tmp + 1; __counter != __fill; ++__counter)
__counter->merge(*(__counter - 1));
swap( *(__fill - 1) );
for(__counter = __tmp;
__counter != __fill && !__counter->empty();
++__counter)
{
__counter->merge(__carry);
__carry.swap(*__counter);
}
__carry.swap(*__counter);
if (__counter == __fill)
++__fill;
}
while ( !empty() );
for (__counter = __tmp + 1; __counter != __fill; ++__counter)
__counter->merge(*(__counter - 1));
swap( *(__fill - 1) );
}
__catch(...)
{
this->splice(this->end(), __carry);
for (int i = 0; i < sizeof(__tmp)/sizeof(__tmp[0]); ++i)
this->splice(this->end(), __tmp[i]);
__throw_exception_again;
}
}
}
@ -530,27 +560,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
list __tmp[64];
list * __fill = __tmp;
list * __counter;
do
__try
{
__carry.splice(__carry.begin(), *this, begin());
for(__counter = __tmp;
__counter != __fill && !__counter->empty();
++__counter)
do
{
__counter->merge(__carry, __comp);
__carry.swap(*__counter);
}
__carry.swap(*__counter);
if (__counter == __fill)
++__fill;
}
while ( !empty() );
__carry.splice(__carry.begin(), *this, begin());
for (__counter = __tmp + 1; __counter != __fill; ++__counter)
__counter->merge(*(__counter - 1), __comp);
swap(*(__fill - 1));
for(__counter = __tmp;
__counter != __fill && !__counter->empty();
++__counter)
{
__counter->merge(__carry, __comp);
__carry.swap(*__counter);
}
__carry.swap(*__counter);
if (__counter == __fill)
++__fill;
}
while ( !empty() );
for (__counter = __tmp + 1; __counter != __fill; ++__counter)
__counter->merge(*(__counter - 1), __comp);
swap(*(__fill - 1));
}
__catch(...)
{
this->splice(this->end(), __carry);
for (int i = 0; i < sizeof(__tmp)/sizeof(__tmp[0]); ++i)
this->splice(this->end(), __tmp[i]);
__throw_exception_again;
}
}
}

View File

@ -0,0 +1,85 @@
// { dg-do run { target c++11 } }
// Copyright (C) 2017 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// 23.2.2.4 list operations [lib.list.ops]
#include <testsuite_hooks.h>
#include <list>
struct ThrowingComparator
{
unsigned int throw_after = 0;
unsigned int count = 0;
bool operator()(int, int) {
if (++count >= throw_after) {
throw 666;
}
return true;
}
};
struct X
{
X() = default;
X(int) {}
};
unsigned int throw_after_X = 0;
unsigned int count_X = 0;
bool operator<(const X&, const X&) {
if (++count_X >= throw_after_X) {
throw 666;
}
return true;
}
int main()
{
std::list<int> a{1, 2, 3, 4};
std::list<int> b{5, 6, 7, 8, 9, 10, 11, 12};
try {
a.merge(b, ThrowingComparator{5});
} catch (...) {
}
VERIFY(a.size() == 8 && b.size() == 4);
std::list<X> ax{1, 2, 3, 4};
std::list<X> bx{5, 6, 7, 8, 9, 10, 11, 12};
throw_after_X = 5;
try {
ax.merge(bx);
} catch (...) {
}
VERIFY(ax.size() == 8 && bx.size() == 4);
std::list<int> ay{5, 6, 7, 8, 9, 10, 11, 12};
try {
ay.sort(ThrowingComparator{5});
} catch (...) {
}
VERIFY(ay.size() == 8);
std::list<X> az{5, 6, 7, 8, 9, 10, 11, 12};
throw_after_X = 5;
try {
az.sort();
} catch (...) {
}
VERIFY(az.size() == 8);
}