[root_ptr] Deterministic Javascript-like memory manager
Hello Boost / Qt, After some experience I have gained in Javascript over the last year, I decided to integrate root_ptr in a quest to replace their garbage collector. So I did and I already have an example. If we mimic the Javascript function scope in C++ then we'll have the following: https://github.com/philippeb8/root_ptr/blob/develop/example/javascript_examp... The application was compiled with: g++ -fpermissive -DBOOST_DISABLE_THREADS -std=c++11 -I../include -I/usr/local/include -L/usr/local/lib -lboost_thread -lboost_system javascript_example1.cpp And it outputs: Scope 0: BEGIN Scope 1: BEGIN A::A(const boost::node_proxy&) A::A(const boost::node_proxy&) A::~A() Scope 1: END A::~A() Scope 0: END Which is exactly what we want. Note that I didn't stress tested it yet and in Brave New World we would be able to integrate it to other languages relatively easily given Javascript is a worse-case scenario. What do you think? Regards, -Phil http://www.fornux.com http://www.finitetheory.com https://www.actatabula.com https://github.com/philippeb8/root_ptr/
On 07/08/2017 07:10 PM, Phil Bouchard via Boost wrote:
Hello Boost / Qt,
After some experience I have gained in Javascript over the last year, I decided to integrate root_ptr in a quest to replace their garbage collector.
So I did and I already have an example. If we mimic the Javascript function scope in C++ then we'll have the following: https://github.com/philippeb8/root_ptr/blob/develop/example/javascript_examp...
The application was compiled with: g++ -fpermissive -DBOOST_DISABLE_THREADS -std=c++11 -I../include -I/usr/local/include -L/usr/local/lib -lboost_thread -lboost_system javascript_example1.cpp
And it outputs: Scope 0: BEGIN Scope 1: BEGIN A::A(const boost::node_proxy&) A::A(const boost::node_proxy&) A::~A() Scope 1: END A::~A() Scope 0: END
Which is exactly what we want. Note that I didn't stress tested it yet and in Brave New World we would be able to integrate it to other languages relatively easily given Javascript is a worse-case scenario.
I just made the example more complex and I am using a cycle: struct A { node_ptr<A> i; node_ptr<int> j; [...] }; int main() { cout << "Scope 0: BEGIN" << endl; { node_proxy x; node_ptr<A> a(x); cout << "Scope 1: BEGIN" << endl; { node_proxy x; node_ptr<A> b = make_node<A>(x, x, "b1"); node_ptr<A> c = make_node<A>(x, x, "c1"); a = b; b = make_node<A>(x, x, "b2"); b->i = b; } cout << "Scope 1: END" << endl; } cout << "Scope 0: END" << endl; } And now I have: Scope 0: BEGIN Scope 1: BEGIN A::A(const boost::node_proxy&, const char*): b1 A::A(const boost::node_proxy&, const char*): c1 A::A(const boost::node_proxy&, const char*): b2 A::~A(): c1 A::~A(): b2 Scope 1: END A::~A(): b1 Scope 0: END That is flawless... If anybody is willing to fool this memory manager then please go ahead! -Phil
On 07/08/2017 10:39 PM, Phil Bouchard via Boost wrote:
That is flawless... If anybody is willing to fool this memory manager then please go ahead!
Actually I think I'll still need to fine tune-it but I'm getting there. Also it'll be better if I reintegrate the old "inclusive" mode (in the master branch) and the user can choose the mode at run-time or compile-time. Thanks, -Phil
On 07/09/2017 11:01 AM, Phil Bouchard via Boost wrote:
On 07/08/2017 10:39 PM, Phil Bouchard via Boost wrote:
That is flawless... If anybody is willing to fool this memory manager then please go ahead!
Actually I think I'll still need to fine tune-it but I'm getting there. Also it'll be better if I reintegrate the old "inclusive" mode (in the master branch) and the user can choose the mode at run-time or compile-time.
Little change here in the code: --- include/boost/smart_ptr/root_ptr.hpp (revision 678) +++ include/boost/smart_ptr/root_ptr.hpp (working copy) @@ -314,7 +314,8 @@ // upscale the proxy of the operand if (px_->depth() < p.px_->depth()) - propagate(p); + p.proxy(* px_); + base::operator = (p); I'm pretty sure I got it right if I add the notion of 'upscaling the scope of a variable'. For example: int main() { cout << "Scope 0: BEGIN" << endl; { node_proxy x; // 1st proxy node_ptr<A> a1 = make_node<A>(x, x, "a1"); cout << "Scope 1: BEGIN" << endl; { node_proxy x; // 2nd proxy node_ptr<A> b1 = make_node<A>(x, x, "b1"); node_ptr<A> b2 = make_node<A>(x, x, "b2"); a1 = b1; // upscale scope of b1 to use 1st proxy b1 = make_node<A>(x, x, "b3"); // scope of b1 will still be associated with the 1st proxy b1->i = b1; // cycle } cout << "Scope 1: END" << endl; } cout << "Scope 0: END" << endl; } Will output: Scope 0: BEGIN A::A(const boost::node_proxy&, const char*): a1 Scope 1: BEGIN A::A(const boost::node_proxy&, const char*): b1 A::A(const boost::node_proxy&, const char*): b2 A::~A(): a1 A::A(const boost::node_proxy&, const char*): b3 A::~A(): b2 Scope 1: END A::~A(): b1 A::~A(): b3 Scope 0: END Now here b1 got "upscaled" because it was once associated with a variable of a higher scope: a1 (closure in Javascript). And you can't downscale a variable to a lower scope so b1 will get destroyed in scope 0. Do you understand what I'm talking about? Thanks, -Phil
On 07/09/2017 02:30 PM, Phil Bouchard via Boost wrote:
int main() { cout << "Scope 0: BEGIN" << endl; { node_proxy x; // 1st proxy node_ptr<A> a1 = make_node<A>(x, x, "a1");
cout << "Scope 1: BEGIN" << endl; { node_proxy x; // 2nd proxy node_ptr<A> b1 = make_node<A>(x, x, "b1"); node_ptr<A> b2 = make_node<A>(x, x, "b2");
a1 = b1; // upscale scope of b1 to use 1st proxy
b1 = make_node<A>(x, x, "b3"); // scope of b1 will still be associated with the 1st proxy b1->i = b1; // cycle } cout << "Scope 1: END" << endl; } cout << "Scope 0: END" << endl; }
Will output:
Scope 0: BEGIN A::A(const boost::node_proxy&, const char*): a1 Scope 1: BEGIN A::A(const boost::node_proxy&, const char*): b1 A::A(const boost::node_proxy&, const char*): b2 A::~A(): a1 A::A(const boost::node_proxy&, const char*): b3 A::~A(): b2 Scope 1: END A::~A(): b1 A::~A(): b3 Scope 0: END
Verdict: I will dissociate root_ptr from Boost and include it in Qt. I am not sure where the whole Boost memory management crew went over the years but the underlying integration with Javascript is the way to go. But thanks Boost for your support over the years. Sincerely, -Phil
On Jul 9, 2017, at 7:07 PM, Phil Bouchard via Boost
I am not sure where the whole Boost memory management crew went over the years but the underlying integration with Javascript is the way to go. But thanks Boost for your support over the years.
Mostly I stick to boost::intrusive_ptr or equivalent in my own C++ code. However, as I developed my own high-level programming language[1], I discovered that having recursive closures implies having reference cycles, and I can no longer say that I’ve never written a mark-and-sweep garbage collector in anger. [1] https://www.vcode.org/ It was essential to maintain the property of timely destruction, though, so it sounds like our respective subsystems have similar requirements. Josh
On 07/09/2017 10:51 PM, Josh Juran via Boost wrote:
On Jul 9, 2017, at 7:07 PM, Phil Bouchard via Boost
wrote: I am not sure where the whole Boost memory management crew went over the years but the underlying integration with Javascript is the way to go. But thanks Boost for your support over the years.
Mostly I stick to boost::intrusive_ptr or equivalent in my own C++ code. However, as I developed my own high-level programming language[1], I discovered that having recursive closures implies having reference cycles, and I can no longer say that I’ve never written a mark-and-sweep garbage collector in anger.
Recursive closures shouldn't be a problem with root_ptr. I'll give it a shot later this week to confirm.
It was essential to maintain the property of timely destruction, though, so it sounds like our respective subsystems have similar requirements.
root_ptr is instantaneous; it's not postponing the destruction of the objects. -Phil
On 07/09/2017 02:30 PM, Phil Bouchard via Boost wrote:
Little change here in the code:
--- include/boost/smart_ptr/root_ptr.hpp (revision 678) +++ include/boost/smart_ptr/root_ptr.hpp (working copy) @@ -314,7 +314,8 @@
// upscale the proxy of the operand if (px_->depth() < p.px_->depth()) - propagate(p); + p.proxy(* px_); +
base::operator = (p);
That change was wrong and was reverted... -Phil
Phil Bouchard wrote:
Verdict: I will dissociate root_ptr from Boost and include it in Qt.
I am not sure where the whole Boost memory management crew went over the years but the underlying integration with Javascript is the way to go. But thanks Boost for your support over the years.
Sincerely, Phil
Hi Phil, Good luck. Please feel encouraged to share the results of including root_ptr in Qt and the lessons learned from that endeavour. Glen
On 07/10/2017 08:38 PM, Glen Fernandes via Boost wrote:
Hi Phil,
Good luck. Please feel encouraged to share the results of including root_ptr in Qt and the lessons learned from that endeavour.
Thanks. Meanwhile further updates will be in this branch: https://github.com/philippeb8/root_ptr/tree/qt Regards, -Phil
On 07/11/2017 12:46 AM, Phil Bouchard via Boost wrote:
On 07/10/2017 08:38 PM, Glen Fernandes via Boost wrote:
Hi Phil,
Good luck. Please feel encouraged to share the results of including root_ptr in Qt and the lessons learned from that endeavour.
Thanks. Meanwhile further updates will be in this branch: https://github.com/philippeb8/root_ptr/tree/qt
Specifically this file: https://github.com/philippeb8/root_ptr/blob/qt/example/javascript_example1.c... I already wrote a manual Javascript to C++ converter algorithm. -Phil
participants (3)
-
Glen Fernandes
-
Josh Juran
-
Phil Bouchard