Sign in to follow this  
quarnster

Reference counting

Recommended Posts

I'm confused how the reference counting works for native functions and for the generic call convention. In my engine I have:

1) Functions returning objects increase the reference count of the returned object.

2) If the argument isn't returned (see 1), the callee increases the reference count of arguments if and only if it stores a copy. This means no unneeded work done, but the caller can't decrease its own reference until after the callee returns, which is exactly how you'd have to do it anyway from any c-like language (Suppose it wasn't reference counted, but a normal pointer, you can't create an object, delete it and then call into a function taking it as an argument can you? You have to create it, call into the function and then delete it)


So the expectation is for example:
[code] // Some function in c++
Shape* sphere = world->CreateSphereShape(1.0); // sphere comes in as count +1
RigidBody* sphereBody = world->CreateRigidBody(1.0, sphere); // spherebody comes in as +1, what sphere is we don't know and don't care, but it'll be at least 1 still
world->AddRigidBody(sphereBody);
// No longer references sphere and sphereBody here so we get rid of our references
sphere->DelRef();
sphereBody->DelRef();

// ... snippity snip ...

void PhysicsWorld::AddRigidBody(RigidBody* node)
{
if (something)
return; // Meh, we didn't want it

// yeah, we wanted it
node->AddRef();
mNodes.push_back(node);
}

// ... snippity snip ...

Something* Something::Blah(Something* arg)
{
// We don't care about this object, but the call convention is to increase the ref count for stuff returned, so that's what we'll do
arg->AddRef();
return arg;
}
[/code]

Now from [url="http://angelcode.com/angelscript/sdk/docs/manual/doc_obj_handle.html"]http://angelcode.com...obj_handle.html[/url] we can read that functions should proactively call AddRef when returning (same as I have), but also call Release on arguments (in other words the caller increases the ref count before calling the function). Is there a specific reason for that? Imagine a long callstack where an object is passed through but only the endpoint actually needs to store the reference. With the AS call convention a reference is added and released in each step in the callstack whereas in my version it's only done where needed. In a multithreaded environment changes to the reference counter needs to be an atomic operation so isn't necessarily free and it's unneeded work anyway. Am I high? Have I missed something? Scripts wouldn't necessarily call into performance critical sections, but the rest of the engine might and if that section happens to be reference counted we've now introduced unneeded overhead.

For the generic call convention we have SetReturnObject and SetReturnAddress which will (if needed) increase the reference count and do nothing to the count respectively. For arguments though release is called always on the arguments.

This means that if I have a native function taking reference counted arguments, I need to decrease the reference count in the native function when calling the native function directly. If I have a wrapper though, I need to undo this release by calling AddRef in the wrapper. In a way that is consistent with the call convention used, but it's still all unneeded work isn't it?

Then there's auto handles which gives some aid here. To automatically fit the AS call convention into mine, I'd define functions as for example:
[code]RigidBody@ CreateRigidBody(float, Shape@+)[/code]

But there's the caveat mentioned:[quote]However, it is not recommended to use this feature unless you can't change the functions you want to register to properly handle the reference counters. When using the auto handles, AngelScript needs to process all of the handles which causes an extra overhead when calling application registered functions.[/quote]
But this is an extra overhead that's not really needed in the first place, is it? Have I missed something?

Share this post


Link to post
Share on other sites
You're spot on.

This is something that I have planned to change for a very long time, though it's not an easy change because it would break backwards compatiblity. For this reason I'm holding off on this change until I'm ready to move to version 3.0.0.

Regards,
Andreas

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this