mirror of
https://gitlab.com/libeigen/eigen.git
synced 2025-06-04 18:54:00 +08:00
Avoid memory leak when constructor of user-defined type throws exception.
The added check `ctorleak.cpp` demonstrates how the leak can be reproduced. The test appears to pass but it is leaking the storage of the (not created) matrix. I don't know how to make this test fail in the existing test suite but you can run it through Valgrind (or another debugger) to verify the leak. $ ./check.sh ctorleak && valgrind --leak-check=full ./test/ctorleak This patch fixes this leak by adding some try-catch-delete-rethrow blocks to `Eigen/src/Core/util/Memory.h`.
This commit is contained in:
parent
339f14b8d1
commit
58687aa5e6
@ -338,15 +338,6 @@ template<> inline void* conditional_aligned_realloc<false>(void* ptr, size_t new
|
|||||||
*** Construction/destruction of array elements ***
|
*** Construction/destruction of array elements ***
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/** \internal Constructs the elements of an array.
|
|
||||||
* The \a size parameter tells on how many objects to call the constructor of T.
|
|
||||||
*/
|
|
||||||
template<typename T> inline T* construct_elements_of_array(T *ptr, size_t size)
|
|
||||||
{
|
|
||||||
for (size_t i=0; i < size; ++i) ::new (ptr + i) T;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \internal Destructs the elements of an array.
|
/** \internal Destructs the elements of an array.
|
||||||
* The \a size parameters tells on how many objects to call the destructor of T.
|
* The \a size parameters tells on how many objects to call the destructor of T.
|
||||||
*/
|
*/
|
||||||
@ -357,6 +348,24 @@ template<typename T> inline void destruct_elements_of_array(T *ptr, size_t size)
|
|||||||
while(size) ptr[--size].~T();
|
while(size) ptr[--size].~T();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \internal Constructs the elements of an array.
|
||||||
|
* The \a size parameter tells on how many objects to call the constructor of T.
|
||||||
|
*/
|
||||||
|
template<typename T> inline T* construct_elements_of_array(T *ptr, size_t size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (i = 0; i < size; ++i) ::new (ptr + i) T;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
destruct_elements_of_array(ptr, i);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*** Implementation of aligned new/delete-like functions ***
|
*** Implementation of aligned new/delete-like functions ***
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@ -376,14 +385,30 @@ template<typename T> inline T* aligned_new(size_t size)
|
|||||||
{
|
{
|
||||||
check_size_for_overflow<T>(size);
|
check_size_for_overflow<T>(size);
|
||||||
T *result = reinterpret_cast<T*>(aligned_malloc(sizeof(T)*size));
|
T *result = reinterpret_cast<T*>(aligned_malloc(sizeof(T)*size));
|
||||||
|
try
|
||||||
|
{
|
||||||
return construct_elements_of_array(result, size);
|
return construct_elements_of_array(result, size);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
aligned_free(result);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, bool Align> inline T* conditional_aligned_new(size_t size)
|
template<typename T, bool Align> inline T* conditional_aligned_new(size_t size)
|
||||||
{
|
{
|
||||||
check_size_for_overflow<T>(size);
|
check_size_for_overflow<T>(size);
|
||||||
T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size));
|
T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size));
|
||||||
|
try
|
||||||
|
{
|
||||||
return construct_elements_of_array(result, size);
|
return construct_elements_of_array(result, size);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
conditional_aligned_free<Align>(result);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** \internal Deletes objects constructed with aligned_new
|
/** \internal Deletes objects constructed with aligned_new
|
||||||
@ -412,7 +437,17 @@ template<typename T, bool Align> inline T* conditional_aligned_realloc_new(T* pt
|
|||||||
destruct_elements_of_array(pts+new_size, old_size-new_size);
|
destruct_elements_of_array(pts+new_size, old_size-new_size);
|
||||||
T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size));
|
T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size));
|
||||||
if(new_size > old_size)
|
if(new_size > old_size)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
construct_elements_of_array(result+old_size, new_size-old_size);
|
construct_elements_of_array(result+old_size, new_size-old_size);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
conditional_aligned_free<Align>(result);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,7 +457,17 @@ template<typename T, bool Align> inline T* conditional_aligned_new_auto(size_t s
|
|||||||
check_size_for_overflow<T>(size);
|
check_size_for_overflow<T>(size);
|
||||||
T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size));
|
T *result = reinterpret_cast<T*>(conditional_aligned_malloc<Align>(sizeof(T)*size));
|
||||||
if(NumTraits<T>::RequireInitialization)
|
if(NumTraits<T>::RequireInitialization)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
construct_elements_of_array(result, size);
|
construct_elements_of_array(result, size);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
conditional_aligned_free<Align>(result);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,7 +479,17 @@ template<typename T, bool Align> inline T* conditional_aligned_realloc_new_auto(
|
|||||||
destruct_elements_of_array(pts+new_size, old_size-new_size);
|
destruct_elements_of_array(pts+new_size, old_size-new_size);
|
||||||
T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size));
|
T *result = reinterpret_cast<T*>(conditional_aligned_realloc<Align>(reinterpret_cast<void*>(pts), sizeof(T)*new_size, sizeof(T)*old_size));
|
||||||
if(NumTraits<T>::RequireInitialization && (new_size > old_size))
|
if(NumTraits<T>::RequireInitialization && (new_size > old_size))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
construct_elements_of_array(result+old_size, new_size-old_size);
|
construct_elements_of_array(result+old_size, new_size-old_size);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
conditional_aligned_free<Align>(result);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,6 +251,8 @@ ei_add_test(bicgstab)
|
|||||||
ei_add_test(sparselu)
|
ei_add_test(sparselu)
|
||||||
ei_add_test(sparseqr)
|
ei_add_test(sparseqr)
|
||||||
|
|
||||||
|
ei_add_test(ctorleak)
|
||||||
|
|
||||||
# ei_add_test(denseLM)
|
# ei_add_test(denseLM)
|
||||||
|
|
||||||
if(QT4_FOUND)
|
if(QT4_FOUND)
|
||||||
|
43
test/ctorleak.cpp
Normal file
43
test/ctorleak.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#include <exception> // std::exception
|
||||||
|
|
||||||
|
struct Foo
|
||||||
|
{
|
||||||
|
int dummy;
|
||||||
|
Foo() { if (!internal::random(0, 10)) throw Foo::Fail(); }
|
||||||
|
class Fail : public std::exception {};
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Eigen
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct NumTraits<Foo>
|
||||||
|
{
|
||||||
|
typedef double Real;
|
||||||
|
typedef double NonInteger;
|
||||||
|
typedef double Nested;
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
IsComplex = 0,
|
||||||
|
IsInteger = 1,
|
||||||
|
ReadCost = -1,
|
||||||
|
AddCost = -1,
|
||||||
|
MulCost = -1,
|
||||||
|
IsSigned = 1,
|
||||||
|
RequireInitialization = 1
|
||||||
|
};
|
||||||
|
static inline Real epsilon() { return 1.0; }
|
||||||
|
static inline Real dummy_epsilon() { return 0.0; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ctorleak()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Matrix<Foo, Dynamic, Dynamic> m(14, 92);
|
||||||
|
eigen_assert(false); // not reached
|
||||||
|
}
|
||||||
|
catch (const Foo::Fail&) { /* ignore */ }
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user