]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[fdxhook] Added gc stuff (hard again for v8), and in the doc too fdxhook
authorFrancis Dupont <fdupont@isc.org>
Tue, 28 Jun 2016 17:23:30 +0000 (19:23 +0200)
committerFrancis Dupont <fdupont@isc.org>
Tue, 28 Jun 2016 17:23:30 +0000 (19:23 +0200)
16 files changed:
src/hooks/external/EXAMPLES
src/hooks/external/NOTES
src/hooks/external/inspect.cc
src/hooks/external/lua/dso.cc
src/hooks/external/lua/tests.cc
src/hooks/external/ocaml/dso.c
src/hooks/external/ocaml/tests.c
src/hooks/external/python/dso.cc
src/hooks/external/python/tests.cc
src/hooks/external/v8/NOTES
src/hooks/external/v8/dso.cc
src/hooks/external/v8/tests.cc
src/hooks/external/v8/voption.cc
src/hooks/external/v8/voption.h
src/hooks/external/v8/vpkt4.cc
src/hooks/external/v8/vpkt4.h

index e51d578304fd4510dea56b2f42a37719aad10c90..fe7305d7cf7d8bb956af76286c4e8cbc05d940ca 100644 (file)
@@ -14,10 +14,10 @@ The 3 (python, ocaml and lua) examples are organized the same way:
 
  - a script written in the external language with a pkt4 -> int
   pkt4_receive function which prints its argument using toText.
-  Note in ocaml the sompiled script (in bytecode or native code)
+  Note in ocaml the compiled script (in bytecode or native code)
   is embedded into the dynamic shared object.
 
- - a tests tool which loads a config with a hook-libraires entry,
+ - a tests tool which loads a config with a hook-libraries entry,
   build a DHCPv4 packet and call the pkt4_receive callout.
 
 So in running order you have:
@@ -50,3 +50,10 @@ embedded language pkt4_receive function is called
 V
 0 (next step continue) is returned
 (here the tests tool exits)
+
+I added a hook forcing a garbage collection both to check whether
+shared pointers are correctly managed and because it can be useful
+in production (look at NOTES for a discussion about two languages'
+garbage collection (non)cooperation). BTW ocaml provides a way to
+take into account dependent memory: caml_{alloc,free}_dependent_memory.
+(ocaml is clearly superior but I have also clearly a bias in favor of it).
index 8cd4105083826cbe0ace8c62a2d4e1c0d3ded484..c243174d56f29622836c73106439ff8a6513cc65 100644 (file)
@@ -63,3 +63,10 @@ Answer:
  ocaml: int, float, string, bytes (same representation than string)
  lua: (number) integer, (number) float, string (include binary)
 
+Question: garbage collection?
+Answer:
+ boost shared pointers and python3 use reference counters. ocaml, lua
+ and v8 use modern tracing systems which are far better but do not
+interact very well with boost shared pointers: objects referenced
+from an external language by an unused value can be freed only when
+the value is collected.
index dd335bff88a75403314f24540aa7fab8c2e62e33..04b0e2193d988ad282f10671ab7f7625a1a9db50 100644 (file)
@@ -37,34 +37,34 @@ int main() {
     cout << "sizeof(IntPtr ::= int*) is " << sizeof(IntPtr) * 8 << " octets\n";
     cout << "sizeof(IntPtr3 ::= int***) is " << sizeof(IntPtr3) * 8 << " octets\n";
     if (sizeof(IntPtr) != sizeof(IntPtr3)) {
-       cout << "unusual size difference between IntPtr and IntPtr3?\n";
+        cout << "unusual size difference between IntPtr and IntPtr3?\n";
     }
     if (sizeof(Four) != 4 * sizeof(IntPtr3)) {
-       cout << "sizeof(Four) is " << sizeof(Four) * 8
-            << " octets and don't match expected " << sizeof(IntPtr3) * 32
-            << " octet?\n";
+        cout << "sizeof(Four) is " << sizeof(Four) * 8
+             << " octets and don't match expected " << sizeof(IntPtr3) * 32
+             << " octet?\n";
     }
     Four one;
     Four zero;
     memset(&zero, 0, sizeof(Four));
     if (memcmp(&zero, &one, sizeof(Four)) != 0) {
-       cout << "Four instance is not initialized to 0 as expected?\n";
+        cout << "Four instance is not initialized to 0 as expected?\n";
     }
     cout << "sizeof(FourPtr ::= Four*) is " << sizeof(FourPtr) * 8 << " octets\n";
     cout << "sizeof(FourSharedPtr ::= shared_ptr<Four> is " << sizeof(FourSharedPtr) * 8 << " octets\n";
     cout << "sizeof(Encap) is " << sizeof(Encap) * 8 << " octets\n";
     if (sizeof(FourSharedPtr) != sizeof(Encap)) {
-       cout << "unusual size difference between Encap and FourSharedPtr\n";
+        cout << "unusual size difference between Encap and FourSharedPtr\n";
     }
     size_t size = sizeof(Encap);
     if (sizeof(Four) < sizeof(Encap)) {
-       cout << "shared pointers are very big?\n";
-       size = sizeof(Four);
+        cout << "shared pointers are very big?\n";
+        size = sizeof(Four);
     }
     Encap encap;
     if (memcmp(&encap, &zero, size) != 0) {
-       cerr << "shared pointers are not initialized to 0???\n";
-       return (-1);
+        cerr << "shared pointers are not initialized to 0???\n";
+        return (-1);
     }
     cout << "shared pointers are initialized to 0 as expected\n";
     return (0);
index e771e90a127a3543375e6e40781e5fb4d2802769..0b0950129c18078b50c5483297d8dbde0fe49899 100644 (file)
@@ -205,4 +205,9 @@ int pkt4_receive(CalloutHandle& handle) {
     return (ret);
 }
 
+// force_gc
+int force_gc(CalloutHandle&) {
+    return (lua_gc(L, LUA_GCCOLLECT, 0));
+}
+
 }
index 5d21d3a54285218d56effd326db73dc119ab1d3a..c1bdf86ff2d1c7c98373bd530f295cca72fa36b5 100644 (file)
@@ -36,6 +36,8 @@ int main() {
     // must be first
     int hi_pkt4_receive = HooksManager::registerHook("pkt4_receive");
     cout << "pkt4_receive is hook#" << hi_pkt4_receive << "\n";
+    int hi_force_gc = HooksManager::registerHook("force_gc");
+    cout << "force_gc is hook#" << hi_force_gc << "\n";
 
     initLogger();
 
@@ -140,7 +142,17 @@ int main() {
     cout << "pkt4_receive callout status " << co_handle->getStatus() << "\n";
     co_handle->getArgument("query4", pkt);
 
+    // call the garbage collector
+    if (!HooksManager::calloutsPresent(hi_force_gc)) {
+        cout << "no callout present for force_gc\n";
+        exit(0);
+    }
+    co_handle->deleteAllArguments();
+    cout << "calling force_gc callout\n";
+    HooksManager::callCallouts(hi_force_gc, *co_handle);
+
     // TODO...
+    cout << "done...\n";
 
     exit(0);
 }
index b42ad1a94bbccf3c9b709128bd159b756d87520b..0a007fd96a353d73ed8fa2d6f6a8afdd14f16be7 100644 (file)
@@ -120,4 +120,11 @@ int pkt4_receive(CalloutHandle& handle) {
     return (Int_val(result));
 }
 
+// force_gc
+extern void caml_minor_collection();
+int force_gc(CalloutHandle&) {
+    caml_minor_collection();
+    return (0);
+}
+
 }
index 5f0f416c313583c04a39f92f9cf255255b17177b..874e3d7f924716aec11a7a5c6daac96cbf19b543 100644 (file)
@@ -49,6 +49,8 @@ int main() {
     // must be first
     int hi_pkt4_receive = HooksManager::registerHook("pkt4_receive");
     cout << "pkt4_receive is hook#" << hi_pkt4_receive << "\n";
+    int hi_force_gc = HooksManager::registerHook("force_gc");
+    cout << "force_gc is hook#" << hi_force_gc << "\n";
 
     initLogger();
 
@@ -153,7 +155,17 @@ int main() {
     cout << "pkt4_receive callout status " << co_handle->getStatus() << "\n";
     co_handle->getArgument("query4", pkt);
 
+    // call the garbage collector
+    if (!HooksManager::calloutsPresent(hi_force_gc)) {
+        cout << "no callout present for force_gc\n";
+        exit(0);
+    }
+    co_handle->deleteAllArguments();
+    cout << "calling force_gc callout\n";
+    HooksManager::callCallouts(hi_force_gc, *co_handle);
+
     // TODO...
+    cout << "done...\n";
 
     exit(0);
 }
index 4a5acdf4f719b8e077da59fee2f7c7d9c6519eb3..5eef247fc77a6b399d00d32fba0b9df31e869805 100644 (file)
@@ -169,4 +169,10 @@ int pkt4_receive(CalloutHandle& handle) {
     return (result);
 }
 
+// force_gc
+int force_gc(CalloutHandle&) {
+    cout << "cpython uses reference counts\n";
+    return (0);
+}
+
 }
index 30e69da571f4e2122b8a7b47826791f167e3e4b2..4609d75cbb69e9191c83a5fde71feff6ef840eda 100644 (file)
@@ -37,6 +37,8 @@ int main() {
     // must be first
     int hi_pkt4_receive = HooksManager::registerHook("pkt4_receive");
     cout << "pkt4_receive is hook#" << hi_pkt4_receive << "\n";
+    int hi_force_gc = HooksManager::registerHook("force_gc");
+    cout << "force_gc is hook#" << hi_force_gc << "\n";
 
     initLogger();
 
@@ -141,7 +143,17 @@ int main() {
     cout << "pkt4_receive callout status " << co_handle->getStatus() << "\n";
     co_handle->getArgument("query4", pkt);
 
+    // call the garbage collector
+    if (!HooksManager::calloutsPresent(hi_force_gc)) {
+        cout << "no callout present for force_gc\n";
+        exit(0);
+    }
+    co_handle->deleteAllArguments();
+    cout << "calling force_gc callout\n";
+    HooksManager::callCallouts(hi_force_gc, *co_handle);
+
     // TODO...
+    cout << "done...\n";
 
     exit(0);
 }
index 5be6a0e0922eca0a0a07dd963b6b5ea54288cab6..97b80038cb25dbb689945c75750165fac16f2be1 100644 (file)
@@ -34,7 +34,8 @@ Manifest:
 
 v8 external API supports only raw pointers.
 
-There is no proof the garbage collecting part is correct. BTW v8 is known
-to not cleanup things (performance issue for Chrome).
+v8 is known to not cleanup things (performance issue for Chrome).
 
 The documentation is nearly useless and the API unstable...
+
+Weak persistent external values are very hairy to manage.
index cbf06d87d883e6a63d6ca7cb9e234d3b46579248..83703eb82e27c2d662c5612b729484a4f6824850 100644 (file)
@@ -285,4 +285,11 @@ int pkt4_receive(CalloutHandle& handle) {
     return (static_cast<int>(ret));
 }
 
+// force_gc
+int force_gc(CalloutHandle&) {
+    isolate_->RequestGarbageCollectionForTesting(
+        Isolate::GarbageCollectionType::kFullGarbageCollection);
+    return (0);
+}
+
 }
index cdd15f03b72a2a5bfab5d0f783a2c40dd1e1ba34..a0b4e4db9dbaac2f711d82155594bcddd6743615 100644 (file)
@@ -30,7 +30,7 @@ const string config =
     "   \"parameters\": "
     "   { \"program\": \"kea\", "
     "     \"script\": \"hook.js\","
-    "     \"flags\": \"--use_strict\" }"
+    "     \"flags\": \"--use_strict --expose_gc\" }"
     " }] }";
 
 // main routine
@@ -38,6 +38,8 @@ int main() {
     // must be first
     int hi_pkt4_receive = HooksManager::registerHook("pkt4_receive");
     cout << "pkt4_receive is hook#" << hi_pkt4_receive << "\n";
+    int hi_force_gc = HooksManager::registerHook("force_gc");
+    cout << "force_gc is hook#" << hi_force_gc << "\n";
 
     initLogger();
 
@@ -142,7 +144,17 @@ int main() {
     cout << "pkt4_receive callout status " << co_handle->getStatus() << "\n";
     co_handle->getArgument("query4", pkt);
 
+    // call the garbage collector
+    if (!HooksManager::calloutsPresent(hi_force_gc)) {
+        cout << "no callout present for force_gc\n";
+        exit(0);
+    }
+    co_handle->deleteAllArguments();
+    cout << "calling force_gc callout\n";
+    HooksManager::callCallouts(hi_force_gc, *co_handle);
+
     // TODO...
+    cout << "done...\n";
 
     exit(0);
 }
index 61d2959b0a6b40d9842a73b6051a8a55ec930699..fb3de23afa3362bf4ee6de293807aa9f0b010d44 100644 (file)
@@ -26,9 +26,9 @@ void
 option_finalize(const WeakCallbackData<Object, v8_option>& data) {
     // This is a critical code to avoid memory leaks
     cout << "option_finalize called\n";
-    Local<External> field =
-        Local<External>::Cast(data.GetValue()->GetInternalField(0));
-    delete static_cast<v8_option*>(field->Value());
+    v8_option* cppobj = static_cast<v8_option*>(data.GetParameter());
+    cppobj->handle.Reset();
+    delete cppobj;
 }
 
 // toString
@@ -70,14 +70,14 @@ Local<Object> make_option(Isolate* isolate, OptionPtr opt) {
     }
 
     // Set the C++ part
-    v8_option* ccpobj(new v8_option());
-    ccpobj->object = opt;
-    Local<External> ptr = External::New(isolate, ccpobj);
+    v8_option* cppobj(new v8_option());
+    cppobj->object = opt;
+    Local<External> ptr = External::New(isolate, cppobj);
     result->SetInternalField(0, ptr);
 
-    // Show the new value to the garbage collector
-    Persistent<Object> gcref(isolate, result);
-    gcref.SetWeak<v8_option>(ccpobj, option_finalize);
+    // Set the V8 part
+    cppobj->handle.Reset(isolate, result);
+    cppobj->handle.SetWeak<v8_option>(cppobj, option_finalize);
 
     return (handle_scope.Escape(result));
 }
@@ -96,7 +96,7 @@ void init_option(Isolate* isolate) {
     Local<Function> tostring;
     if (!Function::New(isolate->GetCurrentContext(),
                        option_tostring).ToLocal(&tostring)) {
-        cerr << "can't create pkt4_tostring\n";
+        cerr << "can't create option_tostring\n";
     }
     templ->Set(isolate, "toString", tostring);
 
index af1ad12f7128179b9bd205d31783362bfff3529b..2198b9209da91d05ac647720644f33ff62d7f399 100644 (file)
@@ -18,6 +18,7 @@ public:
     v8_option();
 
     isc::dhcp::OptionPtr object;
+    ::v8::Persistent< ::v8::Object> handle;
 };
 
 ::v8::Local< ::v8::Object> make_option(::v8::Isolate* isolate,
index 66656d83e6c24eebb9eb7f888372ca17f2ff8447..ebcc3de0fc48acb2fddbe274383eee54046ad83f 100644 (file)
@@ -27,9 +27,9 @@ void
 pkt4_finalize(const WeakCallbackData<Object, v8_pkt4>& data) {
     // This is a critical code to avoid memory leaks
     cout << "pkt4_finalize called\n";
-    Local<External> field =
-        Local<External>::Cast(data.GetValue()->GetInternalField(0));
-    delete static_cast<v8_pkt4*>(field->Value());
+    v8_pkt4* cppobj = static_cast<v8_pkt4*>(data.GetParameter());
+    cppobj->handle.Reset();
+    delete cppobj;
 }
 
 // toString
@@ -71,14 +71,14 @@ Local<Object> make_pkt4(Isolate* isolate, Pkt4Ptr pkt) {
     }
 
     // Set the C++ part
-    v8_pkt4* ccpobj(new v8_pkt4());
-    ccpobj->object = pkt;
-    Local<External> ptr = External::New(isolate, ccpobj);
+    v8_pkt4* cppobj(new v8_pkt4());
+    cppobj->object = pkt;
+    Local<External> ptr = External::New(isolate, cppobj);
     result->SetInternalField(0, ptr);
 
-    // Show the new value to the garbage collector
-    Persistent<Object> gcref(isolate, result);
-    gcref.SetWeak<v8_pkt4>(ccpobj, pkt4_finalize);
+    // Set the V8 part
+    cppobj->handle.Reset(isolate, result);
+    cppobj->handle.SetWeak<v8_pkt4>(cppobj, pkt4_finalize);
 
     return (handle_scope.Escape(result));
 }
index 88a05c436a38e6a1c367c02b136d77f9cdc90545..f2cdb4ee5c59e38c7f24c1b2cd0f7cd2143e7ad8 100644 (file)
@@ -18,6 +18,7 @@ public:
     v8_pkt4();
 
     isc::dhcp::Pkt4Ptr object;
+    ::v8::Persistent< ::v8::Object> handle;
 };
 
 ::v8::Local< ::v8::Object> make_pkt4(::v8::Isolate* isolate,