Preliminary Review Request: boost-memory

What is new? 1. Lock-free free list. 2. Provide both region allocators (boost::auto_alloc, boost::scoped_alloc) and general-purpose allocators (boost::gc_alloc<http://cpp.winxgui.com/boost-gc-alloc> ). Highlights: 1. Allocating memory is very fast. 2. No memory leaks. No need to delete objects manually. Or forgetting to delete objects is allowed. --- What is boost-memory? It provides allocators named "GC Allocator". Note "GC Allocator" isn't GC. GC Allocator is different from STL Allocator. The following is minimum specification for GC Allocator: typedef void (*DestructorType)(void* data); concept GCAllocator { // Allocate memory without given a cleanup function void* allocate(size_t cb); // Allocate memory with a cleanup function void* allocate(size_t cb, DestructorType fn); // Cleanup and deallocate all allocated memory by this GC Allocator void clear(); // Swap two GCAllocator instances void swap(GCAllocator& o); }; For more information about GC Allocator, refer to http://www.codeproject.com/KB/cpp/gc-allocator.aspx

Sorry, I forgot to give some links. To gain the source code, you can: 1. Download from Boost Vault<http://www.boost-consulting.com/vault/index.php?action=downloadfile&filename=boost-memory-0.1.00.zip&directory=Memory&>or http://code.google.com/p/stdext/downloads/list. 2. Svn checkout http://winx.googlecode.com/svn/tags/boost-memory-0.1.00/. 3. Svn checkout http://svn.boost.org/svn/boost/sandbox/memory/. On Wed, May 7, 2008 at 9:29 AM, shiwei xu <xushiweizh@gmail.com> wrote:
What is new?
1. Lock-free free list. 2. Provide both region allocators (boost::auto_alloc, boost::scoped_alloc) and general-purpose allocators (boost::gc_alloc<http://cpp.winxgui.com/boost-gc-alloc> ).
Highlights:
1. Allocating memory is very fast. 2. No memory leaks. No need to delete objects manually. Or forgetting to delete objects is allowed.
---
What is boost-memory?
It provides allocators named "GC Allocator". Note "GC Allocator" isn't GC.
GC Allocator is different from STL Allocator. The following is minimum specification for GC Allocator:
typedef void (*DestructorType)(void* data);
concept GCAllocator { // Allocate memory without given a cleanup function void* allocate(size_t cb);
// Allocate memory with a cleanup function void* allocate(size_t cb, DestructorType fn);
// Cleanup and deallocate all allocated memory by this GC Allocator void clear();
// Swap two GCAllocator instances void swap(GCAllocator& o); };
For more information about GC Allocator, refer to http://www.codeproject.com/KB/cpp/gc-allocator.aspx

AMDG shiwei xu wrote:
typedef void (*DestructorType)(void* data);
concept GCAllocator { // Allocate memory without given a cleanup function void* allocate(size_t cb);
// Allocate memory with a cleanup function void* allocate(size_t cb, DestructorType fn);
// Cleanup and deallocate all allocated memory by this GC Allocator void clear();
// Swap two GCAllocator instances void swap(GCAllocator& o); };
I have a question about exception safety. Suppose that I specify a destructor when I allocate memory, and then, the constructor of the object throws. Is there any way to prevent the destructor from being called, given this interface? Otherwise, the destructor might end up being called on an object that was never initialized. In Christ, Steven Watanabe

Your question is very interesting. I thought it before. In fact I don't know a perfect solution. If the constructor of an object throws, should the destructor be called? class Foo { private: A m_a; B m_b; C m_c; public: Foo() { m_a.init(); m_b.init(); thow std::exception("error"); m_c.init(); } ~Foo() { ... } }; Suppose we initialized m_a and m_b. m_c was uninitialized when the exception throws. If the destructor is called, it may cause a crash. If the destructor isn't called, the allocated memory of m_a and m_b will be leaked. I choose to call the destructor because I think that a crash is easy to be detected and be solved. On Wed, May 7, 2008 at 9:34 AM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
shiwei xu wrote:
typedef void (*DestructorType)(void* data);
concept GCAllocator { // Allocate memory without given a cleanup function void* allocate(size_t cb);
// Allocate memory with a cleanup function void* allocate(size_t cb, DestructorType fn);
// Cleanup and deallocate all allocated memory by this GC Allocator void clear();
// Swap two GCAllocator instances void swap(GCAllocator& o); };
I have a question about exception safety. Suppose that I specify a destructor when I allocate memory, and then, the constructor of the object throws. Is there any way to prevent the destructor from being called, given this interface? Otherwise, the destructor might end up being called on an object that was never initialized.
In Christ, Steven Watanabe

AMDG shiwei xu wrote:
Your question is very interesting. I thought it before. In fact I don't know a perfect solution. If the constructor of an object throws, should the destructor be called?
class Foo { private: A m_a; B m_b; C m_c;
public: Foo() { m_a.init(); m_b.init(); thow std::exception("error"); m_c.init(); } ~Foo() { ... } };
Suppose we initialized m_a and m_b. m_c was uninitialized when the exception throws. If the destructor is called, it may cause a crash. If the destructor isn't called, the allocated memory of m_a and m_b will be leaked.
I choose to call the destructor because I think that a crash is easy to be detected and be solved.
This is not in accord with normal C++ semantics. It is the responsibility of the constructor to make sure that if it fails, it cleans up any resources that it allocated. In Christ, Steven Watanabe

Thank you. I'm mistaken. On Wed, May 7, 2008 at 11:10 AM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
shiwei xu wrote:
Your question is very interesting. I thought it before. In fact I don't know a perfect solution. If the constructor of an object throws, should the destructor be called?
class Foo { private: A m_a; B m_b; C m_c;
public: Foo() { m_a.init(); m_b.init(); thow std::exception("error"); m_c.init(); } ~Foo() { ... } };
Suppose we initialized m_a and m_b. m_c was uninitialized when the exception throws. If the destructor is called, it may cause a crash. If the destructor isn't called, the allocated memory of m_a and m_b will be leaked.
I choose to call the destructor because I think that a crash is easy to be detected and be solved.
This is not in accord with normal C++ semantics. It is the responsibility of the constructor to make sure that if it fails, it cleans up any resources that it allocated.
In Christ, Steven Watanabe

Hello all, I update GC Allocator specification now. See the example named "testExceptionSemantics". The following is the new minimum specification for GC Allocator: typedef void (*DestructorType)(void* data); concept GCAllocator { // Allocate memory without given a cleanup function void* allocate(size_t cb); // Allocate unmanaged memory with a cleanup function void* unmanaged_alloc(size_t cb, DestructorType fn); // Commit unmanaged memory to be managed. void* manage(void* p, destructor_t fn); // Deprecated: allocate memory with a cleanup function void* allocate(size_t cb, DestructorType fn) { return manage(unmanaged_alloc(cb, fn), fn); } // Cleanup and deallocate all allocated memory by this GC Allocator void clear(); // Swap two GCAllocator instances void swap(GCAllocator& o); }; See http://svn.boost.org/trac/boost/ticket/1879#comment:13 Thank Steven Watanabe and Scott McMurray. On Wed, May 7, 2008 at 11:10 AM, Steven Watanabe <watanabesj@gmail.com> wrote:
This is not in accord with normal C++ semantics. It is the responsibility of the constructor to make sure that if it fails, it cleans up any resources that it allocated.

I take a performance comparison of * boost::memory::auto_alloc * boost::memory::scoped_alloc (use tls_block_pool::instance) * boost::memory::scoped_alloc * boost::memory::gc_alloc (auto) * boost::memory::gc_alloc (manually) * boost::pool (auto) * boost::object_pool (auto) * new/delete (gcc) * mt_allocator (gnu c++) Here (auto) means recycling objects when allocators' destructor is called. and (manually) means deleting objects manually. TestCase: performance.cpp<http://winx.googlecode.com/svn/tags/boost-memory-0.1.01/libs/memory/test/test_basic/memory/performance.cpp> Output: output.txt<http://winx.googlecode.com/svn/tags/boost-memory-0.1.01/output/ubuntu4.1.2-gcc-2CPUs%7E2.93GHz-mem%7E1G/output.txt> See: http://winx.googlecode.com/svn/tags/boost-memory-0.1.01/ On Wed, May 7, 2008 at 12:16 AM, shiwei xu <xushiweizh@gmail.com> wrote:
Hello all,
I update GC Allocator specification now. See the example named "testExceptionSemantics".
The following is the new minimum specification for GC Allocator:
typedef void (*DestructorType)(void* data);
concept GCAllocator { // Allocate memory without given a cleanup function void* allocate(size_t cb);
// Allocate unmanaged memory with a cleanup function
void* unmanaged_alloc(size_t cb, DestructorType fn);
// Commit unmanaged memory to be managed. void* manage(void* p, destructor_t fn);
// Deprecated: allocate memory with a cleanup function
void* allocate(size_t cb, DestructorType fn) { return manage(unmanaged_alloc(cb, fn), fn); }
// Cleanup and deallocate all allocated memory by this GC Allocator void clear();
// Swap two GCAllocator instances void swap(GCAllocator& o); };
See http://svn.boost.org/trac/boost/ticket/1879#comment:13
Thank Steven Watanabe and Scott McMurray.

On Tue, May 6, 2008 at 10:47 PM, shiwei xu <xushiweizh@gmail.com> wrote:
Your question is very interesting. I thought it before. In fact I don't know a perfect solution. If the constructor of an object throws, should the destructor be called?
class Foo { private: A m_a; B m_b; C m_c;
public: Foo() { m_a.init(); m_b.init(); thow std::exception("error"); m_c.init(); } ~Foo() { ... } };
Suppose we initialized m_a and m_b. m_c was uninitialized when the exception throws. If the destructor is called, it may cause a crash. If the destructor isn't called, the allocated memory of m_a and m_b will be leaked.
I choose to call the destructor because I think that a crash is easy to be detected and be solved.
This is a non-point. Go read the rules for which destructors are called when exceptions are thrown in different places. If A and B are written properly, there's no problem. And in any case, throwing in a constructor for an automatic variable doesn't call the destructor, so no matter what you think, you shouldn't call the destructor, since it'll break expectations. Here's a simple counter-example: int *bar() { throw 0; return new int(42); } struct foo { int *p; foo() : p(bar()) {} ~foo() { delete p; } }; Quite obviously UB to call that destructor.

You're right. I'm mistaken. :) On Wed, May 7, 2008 at 11:11 AM, Scott McMurray <me22.ca+boost@gmail.com<me22.ca%2Bboost@gmail.com>> wrote:
On Tue, May 6, 2008 at 10:47 PM, shiwei xu <xushiweizh@gmail.com> wrote:
Your question is very interesting. I thought it before. In fact I don't know a perfect solution. If the constructor of an object throws, should the destructor be called?
class Foo { private: A m_a; B m_b; C m_c;
public: Foo() { m_a.init(); m_b.init(); thow std::exception("error"); m_c.init(); } ~Foo() { ... } };
Suppose we initialized m_a and m_b. m_c was uninitialized when the exception throws. If the destructor is called, it may cause a crash. If the destructor isn't called, the allocated memory of m_a and m_b will be leaked.
I choose to call the destructor because I think that a crash is easy to be detected and be solved.
This is a non-point. Go read the rules for which destructors are called when exceptions are thrown in different places. If A and B are written properly, there's no problem. And in any case, throwing in a constructor for an automatic variable doesn't call the destructor, so no matter what you think, you shouldn't call the destructor, since it'll break expectations.
Here's a simple counter-example: int *bar() { throw 0; return new int(42); } struct foo { int *p; foo() : p(bar()) {} ~foo() { delete p; } };
Quite obviously UB to call that destructor. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

shiwei xu wrote:
2. Provide both region allocators (boost::auto_alloc, boost::scoped_alloc) and general-purpose allocators
Hopefully you intend to place those in a library specific namespace inside "::boost". -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org (msn) - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim,yahoo,skype,efnet,gmail

On Wed, May 7, 2008 at 12:10 PM, Rene Rivera <grafikrobot@gmail.com> wrote:
shiwei xu wrote:
2. Provide both region allocators (boost::auto_alloc, boost::scoped_alloc) and general-purpose allocators
Hopefully you intend to place those in a library specific namespace inside "::boost".
I did it. boost::auto_alloc is only a typedef: namespace boost { typedef boost::memory::auto_alloc auto_alloc; };

shiwei xu wrote:
On Wed, May 7, 2008 at 12:10 PM, Rene Rivera <grafikrobot@gmail.com> wrote:
shiwei xu wrote:
2. Provide both region allocators (boost::auto_alloc, boost::scoped_alloc) and general-purpose allocators Hopefully you intend to place those in a library specific namespace inside "::boost".
I did it. boost::auto_alloc is only a typedef:
namespace boost { typedef boost::memory::auto_alloc auto_alloc; };
OK... Why do you need the typedef in ::boost? Note, putting things in the ::boost namespace is not something to do lightly. -- -- Grafik - Don't Assume Anything -- Redshift Software, Inc. - http://redshift-software.com -- rrivera/acm.org (msn) - grafik/redshift-software.com -- 102708583/icq - grafikrobot/aim,yahoo,skype,efnet,gmail

1. Lock-free free list.
i just had a look at the implementation of memory/boost/lockfree/ stack.hpp ... it quite worries me, since it does neither provide any kind of aba prevention, nor of safe memory reclamation (free list, hazard pointer, pass-the-buck) ... best, tim -- tim@klingt.org http://tim.klingt.org The price an artist pays for doing what he wants is that he has to do it. William S. Burroughs

Hello tim, You may wonder that I create a boost::lockfree library. Yes, the name of "boost::lockfree" is from you proposal. I am interested in it, and write a stack class. There is no other stuffs, because boost-memory library only need a lock-free stack. And I'm willing to contribute to lockfree library. Thanks. On Wed, May 7, 2008 at 8:04 AM, Tim Blechmann <tim@klingt.org> wrote:
1. Lock-free free list.
i just had a look at the implementation of memory/boost/lockfree/ stack.hpp ... it quite worries me, since it does neither provide any kind of aba prevention, nor of safe memory reclamation (free list, hazard pointer, pass-the-buck) ...
best, tim
-- tim@klingt.org http://tim.klingt.org

You may wonder that I create a boost::lockfree library. Yes, the name of "boost::lockfree" is from you proposal. I am interested in it, and write a stack class. There is no other stuffs, because boost-memory library only need a lock-free stack. And I'm willing to contribute to lockfree library.
well, i hope, you use the stack in a manner, that the limitations (aba problem, memory reclamation) are not an issue ... i would be in favor of a safe implementation of a stack, though ... i have a version of a stack, using the pass-the-buck algorithm, which i intended to port to boost.lockfree ... maybe i find some time to do so in the next few days ... cheers, tim -- tim@klingt.org http://tim.klingt.org The first question I ask myself when something doesn't seem to be beautiful is why do I think it's not beautiful. And very shortly you discover that there is no reason. John Cage.

You may wonder that I create a boost::lockfree library. Yes, the name of "boost::lockfree" is from you proposal. I am interested in it, and write a stack class. There is no other stuffs, because boost-memory library only need a lock-free stack. And I'm willing to contribute to lockfree library.
well, i hope, you use the stack in a manner, that the limitations (aba problem, memory reclamation) are not an issue ...
i would be in favor of a safe implementation of a stack, though ... i have a version of a stack, using the pass-the-buck algorithm, which i intended to port to boost.lockfree ... maybe i find some time to do so in the next few days ...
here you go: http://tim.klingt.org/git?p=boost_lockfree.git;a=summary lockfree stack with aba prevention and freelist ... cheers, tim -- tim@klingt.org http://tim.klingt.org The composer makes plans, music laughs. Morton Feldman

Thanks tim. I took a look at the boost lockfree library. And I have some suggestions: 1. freelist template <typename T, std::size_t max_size = 64> class freelist : public dummy_freelist<T> { ... }; I think the following is better: template <typename T, typename Alloc = dummy_freelist<T>, std::size_t max_size = 64> class freelist { Alloc m_alloc; }; 2. explicit keyword stack(unsigned int n); => explicit stack(unsigned int n); freelist(std::size_t initial_nodes); => explicit freelist(std::size_t initial_nodes); Best Regards, Shiwei Xu On Thu, May 8, 2008 at 7:14 PM, Tim Blechmann <tim@klingt.org> wrote:
here you go: http://tim.klingt.org/git?p=boost_lockfree.git;a=summary
lockfree stack with aba prevention and freelist ...

1. freelist
template <typename T, std::size_t max_size = 64> class freelist : public dummy_freelist<T> { ... };
I think the following is better:
template <typename T, typename Alloc = dummy_freelist<T>, std::size_t max_size = 64> class freelist { Alloc m_alloc; };
yes, i see your point ... probably it should be changed in a way to use stl-style allocators ....
2. explicit keyword
stack(unsigned int n); => explicit stack(unsigned int n); freelist(std::size_t initial_nodes); => explicit freelist(std::size_t initial_nodes);
that is a good point ... thanks, tim -- tim@klingt.org http://tim.klingt.org Life is really simple, but we insist on making it complicated. Confucius
participants (5)
-
Rene Rivera
-
Scott McMurray
-
shiwei xu
-
Steven Watanabe
-
Tim Blechmann