Skip to content

Commit

Permalink
Cleanup elements when resize fails in deque(n) (#3720)
Browse files Browse the repository at this point in the history
  • Loading branch information
CaseyCarter authored May 30, 2023
1 parent a4bb668 commit 15dd2ab
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 6 deletions.
2 changes: 2 additions & 0 deletions stl/inc/deque
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,9 @@ public:
: _Mypair(_One_then_variadic_args_t{}, _Al) {
_Alproxy_ty _Alproxy(_Getal());
_Container_proxy_ptr12<_Alproxy_ty> _Proxy(_Alproxy, _Get_data());
_Tidy_guard<deque> _Guard{this};
resize(_Count);
_Guard._Target = nullptr;
_Proxy._Release();
}

Expand Down
64 changes: 58 additions & 6 deletions tests/std/tests/GH_002769_handle_deque_block_pointers/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@

using namespace std;

size_t fancy_counter = 0;
size_t counter = 0;

template <class T>
class counting_ptr {
private:
T* p_;

explicit counting_ptr(T* raw_ptr) noexcept : p_(raw_ptr) {
++fancy_counter;
++counter;
}

template <class U>
Expand Down Expand Up @@ -48,8 +48,8 @@ class counting_ptr {
}

~counting_ptr() {
assert(fancy_counter != 0);
--fancy_counter;
assert(counter != 0);
--counter;
}

explicit operator bool() const noexcept {
Expand Down Expand Up @@ -211,17 +211,69 @@ struct ptr_counting_allocator {
#endif // !_HAS_CXX20
};

size_t count_limit = 0;

struct live_counter {
live_counter() {
add();
}
live_counter(const live_counter&) {
add();
}
live_counter& operator=(const live_counter&) = default;
~live_counter() {
remove();
}

static void add() {
assert(counter <= count_limit);
if (counter == count_limit) {
throw 42;
}
++counter;
}
static void remove() {
assert(counter <= count_limit);
assert(counter > 0);
--counter;
}
};

void test_gh_3717() {
// Also test GH-3717: deque(count) would leak elements if a constructor threw during resize(count)

// make sure the counter/limit machinery works properly
assert(counter == 0);
count_limit = 8;
{
deque<live_counter> d{8};
assert(counter == 8);
}
assert(counter == 0);

// verify that deque(n) cleans up live objects on construction failure
try {
deque<live_counter>{16};
assert(false);
} catch (const int i) {
assert(i == 42);
assert(counter == 0);
}
}

int main() {
{
deque<int, ptr_counting_allocator<int>> dq{3, 1, 4, 1, 5, 9};
dq.insert(dq.end(), {2, 6, 5, 3, 5, 8});
}
assert(fancy_counter == 0);
assert(counter == 0);

{
deque<int, ptr_counting_allocator<int>> dq(979, 323);
dq.insert(dq.begin(), 84, 62);
dq.erase(dq.begin() + 64, dq.begin() + 338);
}
assert(fancy_counter == 0);
assert(counter == 0);

test_gh_3717();
}

0 comments on commit 15dd2ab

Please sign in to comment.