concurrent garbage collector

Hey Boost, After struggling with breaking shared_ptr cycles in my app, I wrote a little concurrent garbage collector which uses boost::lockfree::queue. Its actually fairly general purpose, and I've released the code here: https://gist.github.com/wtholliday/5793270 Inspired by my question on stack overflow: http://stackoverflow.com/questions/17116189/concurrent-garbage-collection-fo... I'm also working on releasing my unit test... its just quite dependent on other code. For the interface: the basic idea is you have a RootPtr<T> used to manage the collector's root set (please forgive my propensity for CamelCase). You have to explicitly manage references between collectable objects (via AddEdge and RemoveEdge functions), but I think that could be made safe using another smart pointer type which knows the collectable object which owns it. At this point I'm not too concerned about performance, as my object graphs aren't that big. I bet a GC guru could implement this without using a queue of events. Anyway, this is the kind of thing I'd love to see in boost someday, so it would be great to read your opinions of it :-) cheers - Taylor W. Taylor Holliday, Owner/Founder Subatomic Software, San Francisco

2013/6/18 Taylor Holliday
Hey Boost,
After struggling with breaking shared_ptr cycles in my app, I wrote a little concurrent garbage collector which uses boost::lockfree::queue. Its actually fairly general purpose, and I've released the code here:
https://gist.github.com/wtholliday/5793270
Inspired by my question on stack overflow:
http://stackoverflow.com/questions/17116189/concurrent-garbage-collection-fo...
I'm also working on releasing my unit test... its just quite dependent on other code.
For the interface: the basic idea is you have a RootPtr<T> used to manage the collector's root set (please forgive my propensity for CamelCase). You have to explicitly manage references between collectable objects (via AddEdge and RemoveEdge functions), but I think that could be made safe using another smart pointer type which knows the collectable object which owns it. At this point I'm not too concerned about performance, as my object graphs aren't that big. I bet a GC guru could implement this without using a queue of events.
Anyway, this is the kind of thing I'd love to see in boost someday, so it would be great to read your opinions of it :-)
Looks good at first glance. It would be good to automate work with RemoveRoot/AddRoot somehow. For example RemoveRootAtScopeExit and AddRootAtScopeExit structures could be added. But usability of this solution looks worser than shared_ptr/weak_ptr. Managing reference counting using RemoveRoot/AddRoot/RemoveEdge/AddEdge looks harder that declaring shared_ptr/weak_ptr variable. Can you give some example when GC is easier to use? -- Best regards, Antony Polukhin

Hi Anthony, Thanks for taking a look! RootPtr<T> automates the work of AddRoot and RemoveRoot... is that what you're after? For AddEdge and RemoveEdge, I'm thinking of writing an EdgePtr<T>. One issue is that an EdgePtr could be destroyed during collection, so the EdgePtr's destructor would have to check to see if its being called from the collector thread to avoid spurious RemoveEdge calls. Maybe there's a better way to do that than with thread-specific data. The situation where this GC is easier to use is when you have cycles that aren't easily broken using weak_ptr. This is the case in my app, where the user can create cycles in the graph (resulting in leaks if using shared_ptr). Using weak_ptr for all edges of the graph just creates another situation of manual memory management. cheers - Taylor On Jun 18, 2013, at 10:32 PM, Antony Polukhin wrote:
2013/6/18 Taylor Holliday
: Hey Boost,
After struggling with breaking shared_ptr cycles in my app, I wrote a little concurrent garbage collector which uses boost::lockfree::queue. Its actually fairly general purpose, and I've released the code here:
https://gist.github.com/wtholliday/5793270
Inspired by my question on stack overflow:
http://stackoverflow.com/questions/17116189/concurrent-garbage-collection-fo...
I'm also working on releasing my unit test... its just quite dependent on other code.
For the interface: the basic idea is you have a RootPtr<T> used to manage the collector's root set (please forgive my propensity for CamelCase). You have to explicitly manage references between collectable objects (via AddEdge and RemoveEdge functions), but I think that could be made safe using another smart pointer type which knows the collectable object which owns it. At this point I'm not too concerned about performance, as my object graphs aren't that big. I bet a GC guru could implement this without using a queue of events.
Anyway, this is the kind of thing I'd love to see in boost someday, so it would be great to read your opinions of it :-)
Looks good at first glance.
It would be good to automate work with RemoveRoot/AddRoot somehow. For example RemoveRootAtScopeExit and AddRootAtScopeExit structures could be added.
But usability of this solution looks worser than shared_ptr/weak_ptr. Managing reference counting using RemoveRoot/AddRoot/RemoveEdge/AddEdge looks harder that declaring shared_ptr/weak_ptr variable. Can you give some example when GC is easier to use?
-- Best regards, Antony Polukhin
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 06/17/2013 10:41 PM, Taylor Holliday wrote:
I like the idea. Some minor comments: In the Collect() function the sequence is a static local variable. You may consider making it a normal member variable (you have already declared the "collector" variable as static.) Apropos, you may consider using boost::call_once instead of using a Meyers singleton, because the latter is not thread-safe. In the mark phase you use an std::vector as a stack. It may be better to use std::stack. In the sweep phase you collect a new set and then assign it to the old. You should probably use one of these instead: _nodes.swap(newNodes); or if you are going for a C++11 only solution: _nodes = std::move(newNodes);

Hey Bjorn, Excellent points! I'll make those changes :-) Thanks! - Taylor On Jun 22, 2013, at 1:16 AM, Bjorn Reese wrote:
On 06/17/2013 10:41 PM, Taylor Holliday wrote:
I like the idea.
Some minor comments:
In the Collect() function the sequence is a static local variable. You may consider making it a normal member variable (you have already declared the "collector" variable as static.)
Apropos, you may consider using boost::call_once instead of using a Meyers singleton, because the latter is not thread-safe.
In the mark phase you use an std::vector as a stack. It may be better to use std::stack.
In the sweep phase you collect a new set and then assign it to the old. You should probably use one of these instead:
_nodes.swap(newNodes);
or if you are going for a C++11 only solution:
_nodes = std::move(newNodes);
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (3)
-
Antony Polukhin
-
Bjorn Reese
-
Taylor Holliday