valgrind LEAK SUMMARY: definitely lost with malloc

I got LEAK SUMMARY: definitely lost from valgrind. But as a beginner with valgrind, I do not know to handle this problem.
I think it should be good, when I am using unique pointer.

In the following code I want to
1. calculate how many bytes is needed for my object: DataBlock.
2. aligned_alloc this size (aligned_alloc is for my context, but not relevant here. The valgrind gives me same Leak problem with normal malloc)
3. Then I cast or re interpret this block of the allocated bytes as a Datablock
4. Initial the attributes of datablock (leave out in code)
5. Give back data block as a unique pointer.

Will there be a memory leak problem?
Or it happens to the code, that I left out?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <size_t COLUMN_COUNT>
std::unique_ptr<DataBlock<COLUMN_COUNT>> DataBlock<COLUMN_COUNT>::build() {
  const uint64_t size_of_data_block = sizeof_db + getDataBytes(table_portion, encodings) + padding;  
// some calculation about how many bytes I need to alloc
  uint8_t* data_malloc = (uint8_t*)aligned_alloc(32, size_of_data_block);
  if (data_malloc == NULL) {
    perror("Error allocating memory");
    abort();
  }
  DataBlock<COLUMN_COUNT>* datablock 
= reinterpret_cast<DataBlock<COLUMN_COUNT>*>(data_malloc);
  // some attributes initialized here
  return std::move(std::make_unique<DataBlock<COLUMN_COUNT>>(datablock));
}

Edit: The Datablock is under a template, which has a parameter with the number of the columns.
Some how like that examples:
1
2
3
DataBlock<1>* db = new DataBlock();  // 1 column of data
DataBlock<2>* db = new DataBlock();  // 2 columns of data
DataBlock<1>* db = new DataBlock();  // 3 columns of data 
Last edited on
I assume aligned_alloc() doesn't use new. In that case, std::unique_ptr won't be able to release the pointer correctly. You need to provide a deleter to std::unique_ptr:
1
2
3
4
5
6
7
typedef void (*DataBlock_deleter)(DataBlock *);
typedef std::unique_ptr<DataBlock, DataBlock_deleter> DataBlock_p;

DataBlock_p DataBlock::build(){
    //...
    return DataBlock_p(datablock, [](DataBlock *p){ aligned_free(p); });
}
std::aligned_alloc():
https://en.cppreference.com/w/cpp/memory/c/aligned_alloc

There's also aligned_alloc() from C11.
Last edited on
Hi helios.
Thanks for your reply. Would that be a problem when my DataBlock with a size_t template?
(See the modified code in the main post.)
Last edited on
Hi mbozzi. Thanks and changed to std::aligned_alloc.
This need us std::free(ptr);
And additional to the advice of helios. When I try to use the deleter, I got following error.
(Here I want to use DataBlock<1>)
1
2
3
4
5
6
7
8
9
error: could not convert ‘std::unique_ptr<DataBlock<1>, void (*)(DataBlock<1>*)>(datablock, <lambda closure object>DataBlock<COLUMN_COUNT>::
build(const std::vector<std::vector<long unsigned int> >&) 
[with long unsigned int COLUMN_COUNT = 1]::<lambda(DataBlock<1>*)>{}.

DataBlock<COLUMN_COUNT>::build(const std::vector<std::vector<long unsigned int> >&)
[with long unsigned int COLUMN_COUNT = 1]::<lambda(DataBlock<1>*)>::operator void (*)(DataBlock<1>*)())’ 

from ‘std::unique_ptr<DataBlock<1>, void (*)(DataBlock<1>*)>’ to ‘std::unique_ptr<DataBlock<1>, std::default_delete<DataBlock<1> > >’
Last edited on
Well duh, you can't do std::vector<bool> s = std::vector<int>();

Helios's code is perfect from my view (I imagine you are using the typedef DataBlock_p for everything?), and you are using std::free?
Hi poteto

Here is a bit of detailed code. When I compile it, it will give the error: could not convert above.
Do you see a problem? But I did do somethings like std::vector<bool> s = std::vector<int>();. The error comes from the deleter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template<size_t COLUMN_COUNT>
class DataBlock_e {
private:

  typedef void (*DataBlock_deleter)(DataBlock_e<COLUMN_COUNT> *);

  typedef std::unique_ptr<DataBlock_e<COLUMN_COUNT>, DataBlock_deleter> DataBlock_p;

// ...

  static std::unique_ptr<DataBlock_e<COLUMN_COUNT>> build
(const std::vector<std::vector<uint64_t>> &table_portion) {
// .. calculation the size
  uint8_t* data_malloc = static_cast<uint8_t*>(std::aligned_alloc(32, size_of_data_block));
  if (data_malloc == NULL) {
    perror("Error allocating memory");
    abort();
  }
  DataBlock_e<COLUMN_COUNT>* datablock = 
reinterpret_cast<DataBlock_e<COLUMN_COUNT>*>(data_malloc);

  // handling the attributes
  return DataBlock_p(datablock, [](DataBlock_e<COLUMN_COUNT> *p){ std::free(p); });

}
Last edited on
Your forgot to specify the deleter for the return type.
Hi Peter.
I do not get you point. Would you specify it?
You are returning an object of type std::unique_ptr<DataBlock_e<COLUMN_COUNT>, DataBlock_deleter>,
but the return type of the function is std::unique_ptr<DataBlock_e<COLUMN_COUNT>>.

So my point is that you probably want to change the return type on line 11 to
std::unique_ptr<DataBlock_e<COLUMN_COUNT>, DataBlock_deleter>.
You can of course use your DataBlock_p typedef that you have created for this purpose.
Last edited on
Wow. Fixed.
Lessen learnt: try to use a deleter, which also matches the template.
It's a bit unfortunate that the type needs to change when you want to use a custom deleter, but it has to work like this if we want unique_ptr to have no extra overhead compared to a raw pointer.

The design decision for shared_ptr is different. shared_ptr doesn't have a template parameter for the deleter, instead you can always specify a deleter for each object, without affecting the type.
Last edited on
It's a bit unfortunate that the type needs to change when you want to use a custom deleter, but it has to work like this if we want unique_ptr to have no extra overhead compared to a raw pointer.

On current implementations, an additional requirement for zero-overhead is that the deleter object can be compressed using the empty-base optimization.

Strongly prefer to write custom deleter types. Most of the time, they will be empty, and you'll reap the benefits. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Stateless deleter type: no members - subject to EBO
template< typename T > 
struct c_deleter 
{
  void operator()(T *ptr) const noexcept
  {
    std::free(const_cast<std::remove_cv_t<T>*>(ptr));
  }
};

template <typename T>  
using c_ptr = std::unique_ptr<T, c_deleter<T>>;

c_ptr<std::byte> cacheline_aligned_alloc(std::size_t size)
{
  return c_ptr<std::byte>(static_cast<std::byte *>(
      std::aligned_alloc(LEVEL1_DCACHE_LINESIZE, size)));
}


If zero-overhead isn't compelling enough, since this custom deleter isn't a pointer type and is default constructible, we retain the ability to default-construct the unique_ptr.
Last edited on
Hi mbozzi
Thanks for you contribution. Can you explain you code little bit, maybe with a example?
Can you explain you code little bit

What's unclear about the example I posted already?
Topic archived. No new replies allowed.