« | Main | »

Handling handles

By Jewe | June 3, 2013

This article explains how to deal with the runtime’s JILHandle correctly.

If you are just using the code that the binding code generator has created, then you need not worry about handles. However, if you plan to edit the binding code, then understanding handles and how to correctly use them is very important.

A JILHandle is essentially a small struct that is used to keep track of every object that the runtime has a reference to. For example, if you create an instance of a script class, then variables will not “point” to that instance directly, but to a JILHandle that was created along with the object. The handle will in turn point to the actual object.

The handle’s most important purpose is to keep track of the number of references to the object. So it can immediately be destroyed when no more references exist. This mechanism is used for all objects the runtime deals with, including those that your application creates natively and wants to “import” into JewelScript.

In order for this to work correctly, it is essential that you create a JILHandle for your object only once. It is also important to handle adding and releasing of references to the object correctly.

In addition, once your object is “in the runtime”, you can’t (and need not) manage it’s life cycle anymore. You should never make assumptions on what the script is doing with your object or how long it stays alive. This means your application must never call free() or use C++ delete on an object for which a handle exists. Instead, you can rely on the life time being managed by the runtime.

All functions in the JewelScript API which return a JILHandle to the caller, will automatically add a reference to the object, so the caller need not (and must not) care about adding references. Likewise, whenever your native type returns a handle to JewelScript using NTLReturnHandle(), the API will take care of adding a reference automatically, so you need not (and must not) do that either.

Consequently, you must release your reference to the handle once you are “done” with it. Meaning, if you have obtained a JILHandle from the runtime and don’t want to keep the reference stored in your class, you must use NTLFreeHandle() to release it. If you have returned a handle to the runtime, and you don’t want to keep the reference stored in your class, you must release it.

If your native type has a function that returns a new instance of any class, then you must use NTLNewHandleForObject() to create a handle for this object. However, you must make sure that you only do this once. If more than one handle exists for the same object, this will severely mess up the memory management, and sooner or later lead to a crash.

Creating a handle for a native object requires some careful consideration. If your application does not “own” the object and isn’t allowed to destroy it, then you must not use NTLNewHandleForObject() to create a handle for it. Otherwise the runtime will try to destroy the object once it’s reference count reaches zero, which will usually lead to a crash. This is especially true for objects owned by the system, e.g. device handles, draw contexts, and the likes.

JewelScript offers weak references for these kinds of objects. Your native type can use NTLNewWeakRefForObject() to obtain a weak reference for the object. The function will return a regular JILHandle, and you still need to ensure proper release of these handles. However, if the handle’s reference count reaches zero, the runtime will not destroy the object.

Unlike regular references, you can create multiple weak references for the same object. However, you should make sure that a counted reference to the object exists, too. Or you must manage the object’s life cycle natively. Otherwise you’ll risk a memory leak.

Topics: docs | Comments Off on Handling handles

Comments are closed.