Skip to content

Commit 7abd42b

Browse files
committed
vector_base: fix memory leak in reserve()
Currently, when using reserve() to actually grow the storage in a vector, a new backend storage is allocated, but the old one is never deallocated. A more minor unexpected behavior that also occurred is that the vector would still apply its exponential growth behavior, ie., it when taking a vector of size 3 and calling `.reserve(4)` on it, one would actually end up with a vector of size 6. The logic to allocate the new storage, copy in the existing elements, then destroy the old elements and swap in the new storage (which does to the old storage being destructed and hence deallocated) is, essentially, taken from how `.resize()` works. When growing a vector using `.reserve()`, the new capacity will now be exactly what was specified in the call to `.reserve()`.
1 parent 1af5d90 commit 7abd42b

1 file changed

Lines changed: 32 additions & 1 deletion

File tree

thrust/detail/vector_base.inl

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,38 @@ template<typename T, typename Alloc>
364364
{
365365
if(n > capacity())
366366
{
367-
allocate_and_copy(n, begin(), end(), m_storage);
367+
// compute the new capacity after the allocation
368+
size_type new_capacity = n;
369+
370+
// do not exceed maximum storage
371+
new_capacity = thrust::min THRUST_PREVENT_MACRO_SUBSTITUTION <size_type>(new_capacity, max_size());
372+
373+
// create new storage
374+
storage_type new_storage(copy_allocator_t(), m_storage, new_capacity);
375+
376+
// record how many constructors we invoke in the try block below
377+
iterator new_end = new_storage.begin();
378+
379+
try
380+
{
381+
// construct copy all elements into the newly allocated storage
382+
new_end = m_storage.uninitialized_copy(begin(), end(), new_storage.begin());
383+
} // end try
384+
catch(...)
385+
{
386+
// something went wrong, so destroy & deallocate the new storage
387+
new_storage.destroy(new_storage.begin(), new_end);
388+
new_storage.deallocate();
389+
390+
// rethrow
391+
throw;
392+
} // end catch
393+
394+
// call destructors on the elements in the old storage
395+
m_storage.destroy(begin(), end());
396+
397+
// record the vector's new state
398+
m_storage.swap(new_storage);
368399
} // end if
369400
} // end vector_base::reserve()
370401

0 commit comments

Comments
 (0)