Advertisement

Premature destruction of object in Android

Started by March 07, 2013 02:42 AM
34 comments, last by AgentC 11 years, 9 months ago

Hello,

Until yesterday I used an older version of AngelScript, from around June 2012 and everything worked fine for my purpose. All scripts ran consistently in android + desktop systems, as far as i was concerned. However, I needed the ability to make nested function calls, and therefore needed the new Push/Pop State functionality.

I did the update in today's morning to the latest version of angelscript, put it to run pretty much the same way I used to before but a obscure bug has been introduced. I've tried everything and I can't solve it.

I have a custom type named UIButton, which is a banal ref type, meant to be created and managed by scripts. As I always did, I have a UIButton button; declared globally in a script, which works fine in windows. It is instanced with the context and only destroyed when i release the context at shutdown.

Nevertheless, against the previous behavior, while I run the same script in android, it crashes my process because button is not valid in any time. I wrapped some logs around the ResetGlobalVars function and in the UIButton constructor and destructor, and here's the output:

-> Going to initialize global vars

-> UIButton created

-> UIButton destroyed

-> Finished initializing global vars

As you can see, within the call of ResetGlobalVars, the button is both created and destroyed. I have even tried to call this function several times in a row and the log is always the same, the UIButton instance doesn't stay alive no matter what, in android.

About compilation of the library, for windows it was compiled as-is, changing only the runtime to Multithreaded (Debug) DLL . For android its the same deal,but added AS_MAX_COMPATIBILITY so the generic calls are used. (couldn't make the .S arm code compile yet).

Any help is appreciated, I need to solve this as soon as possible. Granting a safe sandbox environment from scripts is essential to my software.

Bonus Question: Is it possible to avoid crashes when calling a method on a null reference? Thanks

And to the writer of the library, amazing job, its great beyond words, expect to see it in a pretty cool project soon! :))

At first I suspected that a change in as_callfunc_arm.cpp might have caused this bug, but you say you're using AS_MAX_PORTABILITY so that can't be the reason.

Does the same problem happen if you compile the library with AS_MAX_PORTABILITY on Windows?

How has the global variable been declared in the script? Is it just a declaration without any specific initialization, e.g. 'UIButton button;'? The bytecode sequence for that would look like this:


 
    0   1 *    SUSPEND
    1   1 *    CALLSYS  31           (UIButton@ _beh_2_())         // call factory function
    3   1 *    STOREOBJ v1                                                       // store handle in local variable
    4   1 *    PshVPtr  v1                                                            // push the pointer in the variable onto the stack
    5   2 *    PGA      10833368                                                 // push the address of the global variable on the stack
    7   3 *    REFCPY   0x3e6f78                                               // copy the handle to global variable, and increase the reference
    9   2 *    PopPtr                                                                    // remove the top pointer from the stack
   10   1 *    FREE     v1, 4091768                                            // release the handle in the local variable
   12   1 *    RET      0

Can you add log messages in the AddRef and Release behaviours so we can see if they are called as they should?

Unfortunately I do not have a development environment for Android to test this on, so I'll need your help in figuring out the problem.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement

So I updated my Android compiler environment and tried again with it, and verified it still happened.

Now about the resolution, I will test those things and get back to you next.

RIght now, I managed to compile angelscript for android ndk without AS_MAX_PORTABILITY, for the ARM architecture and im experimenting with this, but im having trouble.

In android, using native calls instead of generic ones causes crashes whenever i call something from the scripts.

script: test.as

void test()

{

int i = 0; i += 5; // primitive operations work fine and the test() function ends normally without crash

Vec2f position; // crashes the program

print("hello world"); //crashes the program

}

As you can read from the comments, the script function compiles and executes fine, primitive operations work, but nothing i exported runs. even a simple stub global function that I export with asCALL_CDECL will crash the program. Is this a bug with ARM native calls?

Now, answering to your reply:

I tried AS_MAX_PORTABILITY in windows, and the same bug is reproduced exactly the same way. However, I use aswrappedcall and the factory/add/release functions are the same for either native and generic conventions.

I logged the ref behaviors and there is something weird happening:

=> Before ResetGlobalVars

=> [RefCounter:057265A4]: Init with reference counter at 1

=> [RefCounter:05726700]: Init with reference counter at 1

=> UIButton Constructor

=> [RefCounter:05726598]: Reference added: Now 1

=> [RefCounter:05726598]: Reference removed: Now 0 (Releasing)

=> UIButton Destructor

=> After ResetGlobalVars

This is very weird. First, there are two inits and I don't know where they come from, I only have one global variable, UIButton button; declared outside any function.

Then, its mem address doesnt match the later one (unless this is normal with polymorphism? )

Finnaly, there is a AddRef call that puts the ref counter at 1, which is impossible since it begins at 1, and if it ever reaches 0, it releases..

Could this be something wrong with my constructors? They are declared in the plain old way "Constructor()", the constructor chain is: UIButton() calls UIControl() which calls RefCountable().

The destructor in RefCountable is declared virtual.. The UIButton is properly initialized in the constructor, the ref count goes to 1, then somehow it is 0, when addRef is called on the exact same address.. This didn't happen with the build of angelscript i had before..

Let's leave the native calling conventions on Android to the side for a while. I suspect this could be just a problem with the as_config.h, but it may very well be caused by the problem you're having with AS_MAX_PORTABILITY, so let's concentrate on the problem with AS_MAX_PORTABILITY first.

That the problem with AS_MAX_PORTABILITY happens on Windows as well makes it easier, as I should be able to reproduce the problem.

The logging you added definitely looks weird, but I need further information from you.

What does the factory function, addref and release functions look like in your UIButton class? Can you show the code? Can you show the actual code you use to register them with AngelScript?

You seem to be using inheritance to implement the reference counting. Is the UIButton inheriting from multiple parent classes? Does any of the parent classes inherit from multiple parent classes?

Finally, would it be possible for you to write a small example to reproduce the problem in an isolated way?

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I dont have an easy way to have an isolated and complete example to reproduce this, as all the code is intrincate within my engine, but I can help with all you said:


// All-purpose default constructor factory
template<typename T>
T* genericFactory()
{
cout<<"instancing ref type"<<endl;
return new T();
}


/// Adds a reference
void RefCountable::addReference(){
refCount++;
cout<<"[RefCount:"<<this<<"] Add: "<<refCount <<endl;
};


/// Removes a reference and destroys if necessary
void RefCountable::removeReference(){
cout<<"[RefCount:"<<this<<"] Released: "<<refCount - 1<<endl; 
if(--refCount == 0){
delete this;
}
};
 engine->getASEngine()->RegisterObjectType("UIButton", sizeof(UIButton), asOBJ_REF);


if(engine->getPortableMode())
{
engine->getASEngine()->RegisterObjectBehaviour("UIButton", asBEHAVE_FACTORY, "UIButton@ f()", WRAP_FN(genericFactory<UIButton>), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectBehaviour("UIButton", asBEHAVE_ADDREF, "void f()", WRAP_MFN(UIButton, addReference), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectBehaviour("UIButton", asBEHAVE_RELEASE, "void f()", WRAP_MFN(UIButton, removeReference), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectBehaviour("UIButton", asBEHAVE_IMPLICIT_REF_CAST, "UIControl@ f()", WRAP_OBJ_LAST(UIButtonRefCast), asCALL_GENERIC);


  engine->getASEngine()->RegisterObjectMethod("UIButton", "void bindSignal(const string &in, Slot@)", WRAP_MFN(UIButton, bindSignal), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectMethod("UIButton", "void setPosition(float,float)", WRAP_MFN(UIButton, setPosition), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectMethod("UIButton", "void setSize(float,float)", WRAP_MFN(UIButton, setSize), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectMethod("UIButton", "void setLabel(const string &in)", WRAP_MFN(UIButton, setLabel), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectMethod("UIButton", "void setProperty(const string &in, const string &in)", WRAP_MFN(UIButton, setRawProperty), asCALL_GENERIC);


}
 

About inheritance:

UIButton -> UIControl

UIControl -> Animable, sigc::Trackable, RefCountable

All constructors are chained properly.

This behavior is very weird. For one call to genericFactory(), RefCountable constructor is called 4 times in different addresses. And we are only talking about 1 global variable.

Advertisement

Does Animable or sigc::Trackable also inherit from RefCountable? This is the only way I can think of for the RefCountable constructor being called multiple times at different addresses.

I assume the constructor of RefCountable initializes the refCount to 1. Is this correct?

The methods addReference and removeReference are declared as virtual in the RefCountable class, aren't they?

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Animable and sigc::Trackable do NOT inherit RefCountable.

And yes, the constructor of RefCountable inits at 1 (pretty much as described in your docs)

addReference and removeReference are just plain methods, not virtual. I figured that their behavior is fixed so there is no point in making them virtual..

I wonder if it is not the templates in the autowrappers that are playing tricks on us.

Can you try with a manual implementation of the wrappers?


void Wrap_UIButton_factory(asIScriptGeneric *gen)
{
   *reinterpret_cast<UIButton**>(gen->GetAddressOfReturnLocation()) = new UIButton();
}
 
void Wrap_UIButton_addReference(asIScriptGeneric *gen)
{
  reinterpret_cast<UIButton*>(gen->GetObject())->addReference();
}
 
 
void Wrap_UIButton_removeReference(asIScriptGeneric *gen)
{
  reinterpret_cast<UIButton*>(gen->GetObject())->removeReference();
}
 
engine->getASEngine()->RegisterObjectBehaviour("UIButton", asBEHAVE_FACTORY, "UIButton@ f()", asFUNCTION(Wrap_UIButton_factory), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectBehaviour("UIButton", asBEHAVE_ADDREF, "void f()",  asFUNCTION(Wrap_UIButton_addReference), asCALL_GENERIC);
engine->getASEngine()->RegisterObjectBehaviour("UIButton", asBEHAVE_RELEASE, "void f()",  asFUNCTION(Wrap_UIButton_removeReference), asCALL_GENERIC);

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

What if I told you that solved the problem? RAGE MODE: ON! :))

Thanks a lot for the patience, that actually solved the problem as it runs now in android and windows as well, guess I ll have to refactor the remaining types, unless you have a more elegant solution?

Now that problem 1 is solved (bogus, I still dont know why!), can you tell me if it is possible to protect null handles? That when someone does UIButton@ button; and calls a method on it, the whole program doesnt crash?

And to finalize, any tips on the ARM issue? Everything seems to be allright with the library, but at the first attempt to call a native function in android it crashes the hell out. Do I benefit from these native calls or generic is just enough? I wonder.

Thanks again!

This topic is closed to new replies.

Advertisement