Shared_ptr does not free user malloc on stream
In my code, I am creating shared_ptr
inside a lambda to save the PNG file as a background task. Unfortunately, even though I have a custom deleter for it shared_ptr
, it seems the bytes are not deallocated correctly.
The code I am using to create shared_ptr
:
std::shared_ptr<GLubyte> buffer = std::shared_ptr<GLubyte>((GLubyte*) malloc(*dataLength), [](GLubyte* buffer) {
free(buffer);
});
And to save the file and finally free it:
std::thread t([=, buffer = std::move(buffer)]() mutable {
bool done = writePNGFileFromBuffer(path, buffer.get(), width, height);
return done;
});
t.detach();
I tried to put buffer.reset()
inside a lambda but although the zero memory buffer is not freed. I also tried changing the creator function to something like:
std::shared_ptr<GLubyte> buffer = std::shared_ptr<GLubyte>((GLubyte*) malloc(*dataLength), std::free);
But it doesn't work either. Now I am using lambda deleter because then I can try to put a breakpoint inside and check the call free
, but the memory is still not released.
Also, I have confirmed that the release works if I put a free(buffer.get())
lambda inside, but it doesn't make sense to me because I use shared_ptr
to avoid things like this.
Can you help me free this buffer? Thank you very much.
source to share
I wrote this little test posting to prove that the new / delete was done correctly.
Note the use of new / delete [] in the buffer constructor. Your use of malloc / free gives the code a bad smell. Resorting to free (ptr.get ()) hides another boolean problem that you haven't solved. If you leave this in the program, he will bite you later.
proxy
acts as a replacement for GLubyte, which counts the allocated and destroyed, so I can confirm with help assert
that each construct has a corresponding destruction.
#include <iostream>
#include <memory>
#include <thread>
#include <cassert>
#include <atomic>
using namespace std;
#define USE_PROXY 1
struct proxy
{
proxy() {
++_count;
}
~proxy() {
--_count;
}
static std::atomic<size_t> _count;
};
std::atomic<size_t> proxy::_count = { 0 };
#if USE_PROXY
using GLubyte = proxy;
#else
//using GLubyte = uint8_t;
#endif
bool writePNGFileFromBuffer(const char* path, const GLubyte* bytes, int width, int height)
{
return true;
}
auto main() -> int
{
{
int dataLength = 10000;
auto buffer = std::shared_ptr<GLubyte>(new GLubyte[dataLength],
[](GLubyte* p) { delete[] p; });
const char* path = "/tmp/foo";
int width = 100, height = 100;
std::thread t([=, buffer = std::move(buffer)]() mutable {
bool done = writePNGFileFromBuffer(path, buffer.get(), width, height);
return done;
});
t.join();
assert(!buffer);
}
assert(proxy::_count == 0);
return 0;
}
for further consideration, you may have wondered how you can handle a failure in a function write....
. The return value is currently being thrown. There are several ways we could deal with this: one is to supply a lambda that can be called when the write operation is complete. Another is to port the write operation to std :: async.
bool writeSharedPNGFileFromBuffer(const char* path, shared_ptr<const GLubyte> bytes, int width, int height)
{
return writePNGFileFromBuffer(path, bytes.get(), width, height);
}
auto main() -> int
{
{
int dataLength = 100;
auto buffer = std::shared_ptr<GLubyte>(new GLubyte[10000], [](GLubyte* p) { delete[] p; });
const char* path = "/tmp/foo";
int width = 100, height = 100;
auto f = std::async(launch::async, writeSharedPNGFileFromBuffer, path, move(buffer), width, height);
// f is a std::future - a handle to somewhere the result will eventually land.
// perform main thread work here...
// ... and get the return value when we're ready to deal with it
auto written = f.get();
cout << "written: " << written << endl;
assert(!buffer);
}
assert(proxy::_count == 0);
return 0;
}
source to share