On Thu, Oct 13, 2011 at 7:37 PM, Nemanja Stojanovic
<fingerp...@gmail.com> wrote:
exactly is it called? When the GC disposes of the js object bound to the C++ object?
IFFFFFF GC disposes (often it does not!).
what i always do is add the following binding to each ClassCreator-bound type:
typedef ClassCreator<T> CC;
CC & cc(CC::Instance());
cc("destroy", CC::DestroyObjectCallback);
(Though "destroy" sometimes gets a different name.) That function will effectively delete the native immediately and detach its ptr from its JS object in such a way that using the JS-side object later will fail. .g.:
var x = new MyNative();
x.destroy();
x.nonNativeFunction(); // okay (no native 'this' required)
x.nonClassMethodNativeFunction(); // okay (no native 'this' required)
x.boundClassMethod(); // will throw a JS exception
Most of the classes I want to expose to JS should exist independently, I would like the engine to manage their lifetime. I'm wondering, is it safe to leave ClassCreator_Factory::Delete empty?
That _will_ cause a leak (unless you're serving a singleton - in that case Delete() should normally do nothing!). The Delete() op _is_ the clean-up routine for that class. v8 (GC) triggers the destruction, but delegates the cleanup of the native half of the binding to ClassCreator, which routes it through the Delete() op for the specific type. So you can still rely on GC to clean up, but Delete() should certainly free any instance-specific resources for the binding (normally by simply calling 'delete' on the argument passed to it, but when binding C structs we need to call a type-specific destructor or free()).
And also, is it safe to manually delete those classes when needed? The js_self handle is disposed of in the destructor, but the js code might still hold a handle to the object, and someone might still try to call a method or something.
The bindings are set up to throw a JS exception in that case. When gc finalizes an object (or the CC::DestroyObjectCallback() is called), ClassCreator clears out the "internal field" where the native pointer is stored. Future calls to bound _member_ functions via that JS handle will try to extract it, find a NULL pointer, and then throw a JS exception. Calls to non-member bound functions generally will _not_ fail the same way because they (normally) do not have/need a native 'this' object. Those which do need a native 'this' can check for it like this:
(inside an InvocationCallback impl...)
T * self = CastFromJS<T>( argv.This() );
if(!self) { ... error ... }
calls obj.kill(). The object will now be out of the list of objects, but should it be deleted? Or should I leave it there and wait 'till Delete gets called?
In my experience, any type-specific destructor, like GameObject.kill(), Stream.close(), ByteArray.finalize(), etc., should also free the native pointer (exception: singletons or shared instances whose lifetimes are managed somewhere else in native code).
How would you suggest I manage the lifetime of such objects?
Most of the native types i wrap (all but 1 or 2 of them) have very specific destruction requirements, and therefore i always add a binding for an explicit destructor function. e.g. when wrapping a db, all Statements it creates _must_ be finalized _before_ the db handle itself is closed (or undefined behaviour results). Relying on GC in such a case is just plain dangerous. For other types, like a ByteArray, relying on GC is not dangerous, it just leaks some memory until GC kicks in (IF it ever kicks in - v8 often _never_ GCs for short-running apps).
Whether or not waiting on GC is okay for your GameObject depends partly on its destruction requirements. For example, if GameObject is owned by a parent GameEngine object, then the GameObject should (must?) be cleaned up before the GameEngine is destroyed. Relying on GC would give undefined results because it cannot know which order to destroy them. Note that v8 does not automatically run GC at shutdown, it just exits and relies on the OS to clean up any remaining resources. This is not generally problematic for types which do _not_ require a destructor call for proper behaviour (e.g. a ByteArray class) but is problematic for types which require a destructor call for well-defined behaviour (like a db or file handle, or any type which might hold pointers to such resources).