- 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:
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).
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.
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);
return (ret);
}
+// force_gc
+int force_gc(CalloutHandle&) {
+ return (lua_gc(L, LUA_GCCOLLECT, 0));
+}
+
}
// 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();
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);
}
return (Int_val(result));
}
+// force_gc
+extern void caml_minor_collection();
+int force_gc(CalloutHandle&) {
+ caml_minor_collection();
+ return (0);
+}
+
}
// 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();
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);
}
return (result);
}
+// force_gc
+int force_gc(CalloutHandle&) {
+ cout << "cpython uses reference counts\n";
+ return (0);
+}
+
}
// 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();
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);
}
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.
return (static_cast<int>(ret));
}
+// force_gc
+int force_gc(CalloutHandle&) {
+ isolate_->RequestGarbageCollectionForTesting(
+ Isolate::GarbageCollectionType::kFullGarbageCollection);
+ return (0);
+}
+
}
" \"parameters\": "
" { \"program\": \"kea\", "
" \"script\": \"hook.js\","
- " \"flags\": \"--use_strict\" }"
+ " \"flags\": \"--use_strict --expose_gc\" }"
" }] }";
// main routine
// 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();
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);
}
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
}
// 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));
}
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);
v8_option();
isc::dhcp::OptionPtr object;
+ ::v8::Persistent< ::v8::Object> handle;
};
::v8::Local< ::v8::Object> make_option(::v8::Isolate* isolate,
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
}
// 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));
}
v8_pkt4();
isc::dhcp::Pkt4Ptr object;
+ ::v8::Persistent< ::v8::Object> handle;
};
::v8::Local< ::v8::Object> make_pkt4(::v8::Isolate* isolate,