Jump to content

Unigine::Ptr deallocation behavior


photo

Recommended Posts

Hello,

 

I am having a problem with the Unigine::Ptr class. I am using it store the Controls exported from script space. When the world is shutdown, this becomes an invalid pointer. When I try to destroy the Ptr class, it attempts to deallocate that invalid pointer. :) So, clearly Unigine world runtime is managing this memory. If you are familiar with boost libraries, this is a case where you might use a weak_ptr, because my application will outlive the owner of the resource (unigine world runtime).

 

I am trying to allow the unigine engine to perform the world_reload command correctly without breaking my application which stores a Ptr<Controls> so that my custom app logic can modify it. I have set up a callback from the world's shutdown() which notifies me that the world has be reloaded. So, I re-initialize all my data structures with fresh pointers to the new unigine runtime's memory. This is where the problem arises, I am assigning a new Ptr<Controls> to an old Ptr<Controls>. Here, destruction occurs before assignment, which is correct behavior.

 

I do have two instances of the Ptr<Controls>, which should be pointing to the same place in memory, which are stored in separate objects. Though, this shouldn't be causing the problem because assigning a new Ptr<Controls> to one of the instances should be allowed, and deallocation should not even occur. Then, when assigning a new Ptr<Controls> to the second instance invokes destruction, which is where the problem occurs. Interestingly, instead of crashing, the application stalls on the dellocation call UniginePtr.h:104.

 

I guess what I am asking for is an option to use the Unigine::Ptr as a weak_ptr. Or, a way to tell it explicitly not to try to deallocate the memory.

Or, any other method you may have to solve this problem? Or, perhaps I'm doing something wrong?

 

 

 

 

Thanks again!

 

 

 

Michael Zhang

Link to comment

Hello,

The following way may be more effective: before world unloading you have to notify the C++ logic part about world shutdown.

On the C++ side you have to release all pointers. After the world initialization you can call C++ function to bind all pointers.

Link to comment

Mm.. yes I considered that. Was hoping for a better way,... because when the C++ logic becomes larger, and it becomes harder to track the UniginePtrs, could be a hassle. I could just destroy all Unigine related objects and reinstantiate them all, but... this makes keeping data across world-reloads somewhat more difficult.

 

In the meantime, what you suggested is what I am doing.

 

THanks!

Link to comment

Hello,

The following way may be more effective: before world unloading you have to notify the C++ logic part about world shutdown.

On the C++ side you have to release all pointers. After the world initialization you can call C++ function to bind all pointers.

 

Hm... so, I seem to be running into some problems here. I am releasing the only Ptr<Controls> when the shutdown() is called. However, this for some reason causes Unigine to freeze :)

 

Any idea what's going on? I can confirm that the destruction of the Ptr<> is occuring, and the release() and deallocate() finished running.

After the shutdown_callback() returns control back to Unigine, it freezes. I am doing nothing within that call back except for releasing the Ptr<Controls>. You might guess that Unigine is somehow trying to destroy the object twice.

 

int shutdown() {

Heaven::shutdown();
Interface::shutdown();
Character::shutdown();
Controls::shutdown();

Core.World.shutdown();
return 1;
}

But, as you can see here, the character and his controls and the entire control system are released in their respective shutdown() calls prior to the shutdown_callback(). So, after returning from the callback, I would expect the shutdown to complete immediately. What occurs next in the world_reload script? Here is my shutdown callback:

void reload(){
	BOOST_LOG_TRIVIAL(trace) << "World shutdown callback to: World.reload()";
	engine->pushWorld(); // are these needed here? I assume so?
	character.reset(); // a shared_ptr release which points to an object storing the Ptr<Controls> which is destroyed as part of Character destruction
	engine->popWorld(); // are these needed here? I assume so?
	reloaded = true;
	initialized = false;
}

Are the engine push/pop needed here? I assume so because, my simplified main loop looks like below, and the callback is actually called as a result of the engine->update() step?

	
engine->pushWorld();
// do stuff
engine->popWorld();
engine->update();
engine->render();
engine->swap();

 

Any suggestions?

Link to comment

Hm... so, I seem to be running into some problems here. I am releasing the only Ptr<Controls> when the shutdown() is called. However, this for some reason causes Unigine to freeze :)

 

Any idea what's going on? I can confirm that the destruction of the Ptr<> is occuring, and the release() and deallocate() finished running.

After the shutdown_callback() returns control back to Unigine, it freezes. I am doing nothing within that call back except for releasing the Ptr<Controls>. You might guess that Unigine is somehow trying to destroy the object twice.

 

Please create small example which reproduces your problem and we will try to fix it.

 

Are the engine push/pop needed here? I assume so because, my simplified main loop looks like below, and the callback is actually called as a result of the engine->update() step?

	
engine->pushWorld();
// do stuff
engine->popWorld();
engine->update();
engine->render();
engine->swap();

 

If you don't assign extern class pointers on Variables or get extern class pointers from Variable then you can omit push/pop functions.

Link to comment

Please create small example which reproduces your problem and we will try to fix it.

Attached is a simple test scene demonstrating the error I get.

Just compile the source folder using your compiler of choice, it should only depend upon Unigine library.

 

To reproduce the error:

1) Run Application : Here you should see the agent falling infinitely...

2) Open console and run: world_reload

3) Crash!

 

 

Thanks for your help! :)

Ptr Test.zip

Link to comment

Oh, sorry, I cleaned up an older version of the problem before I was correctly releasing resources before unigine shutdown. Please use this attached file.

 

This version is the more recent problem, where it stalls on engine->render()

Deallocation of the pointer is not causing the crash.

 

Attached.

 

 

Thanks :)

Ptr Test.zip

Link to comment

Your solution has many references to external folders on your PC, so i created simple solution, launched application and error was occurred: lost file #include <genesis/scripts/custom_character.h>

Please, when you create a test scene before upload try to make sure that test scene does not contain the references to any files on your computer. Best way to check this it is to replace your test scene folder and try to launch.

Link to comment

Your solution has many references to external folders on your PC, so i created simple solution, launched application and error was occurred: lost file #include <genesis/scripts/custom_character.h>

Please, when you create a test scene before upload try to make sure that test scene does not contain the references to any files on your computer. Best way to check this it is to replace your test scene folder and try to launch.

 

Here you are, I removed any references I could find, and included the missing custom_character.h, which is taken from the unigine Character demo :blink: I am not particularly familiar with the visual studio IDE yet, so there may yet be references that I did not notice. However, your simple solution sounds like it works, it's a really simple test application with no actual dependencies besides unigine. The included custom_character.h should solve your problem.

Ptr Test (2).zip

Link to comment

Inside code there are reference:

character = new Custom::CustomCharacter("demos/genesis/agent.character");

I'll remove it.

 

But there are some troubles:

in agent.caracter:

<skin>
	<node>demos/genesis/nodes/agent.node</node>
	<scale>2 2 2</scale>
</skin>

<!-- attachments -->
<attachments>
	<attachment name="pistol" locator="weapon">demos/genesis/nodes/pistol.node</attachment>
</attachments>

       <animation start="0" end="-1" speed="15">demos/genesis/animation/agent_idle_turn_lft_90.sanim</animation>

I tried to add our animation and agent.node, but it seems that difference is occur.

Please, upload your animation folder and nodes folder

Link to comment

Ah, these dependencies are hard to track ;) I'm just using the existing files used in your sample projects, so I tend to forget that those files reference other files.

 

I am using unaltered version of the animation and nodes ... so surprised that that would cause problems.

Nonetheless, my versions are attached. Hope this works!

animation nodes.zip

Link to comment

Inside code there are reference:

character = new Custom::CustomCharacter("demos/genesis/agent.character");

I'll remove it.

 

If you remove that, then the character will not be instantiated? Then, there will not be a valid Controls object to retrieve...

The referenced file is "agent.character" which was included into the zip file. You should change that path to a relative path according to whatever data folder structure you have set up so that it points to the agent.character file.

Link to comment

Your code contains an error: if you create the objects (in your case it is Custom::CustomCharacter character) in the script side do not free this objects in C++ side. When you use Unigine::Ptr class, which frees the object automatically, you get a double release: on the scrip side and C++. The firs way is using class_remove function on the script side, which prevents an object from being handled by the scripting system, then you can free this object on the C++ side. Or the second way: don't use the smart pointers (Unigine::Ptr), use the simple pointer:

Unigine::Controls *controls;
controls = static_cast<Unigine::Controls*>(controlsVariable.getExternClassObject(controlsVariable.getExternClassType()));

 

and remove the following code:

void release(){
	//delete controls;
}

 

Just in case the working code of World.h in the attachment.World.h

Link to comment

Aha! OKay, as I expected, just a matter of who is managing what resource. Now I know how to correctly manage these resources. I think this is an important topic to include in your documentation ! ;)

 

Is it safe to assume all Objects returned by the UNigine runtime are managed by Unigine? And consequently should not be released by C++ code?

 

And, further clarification: for the Variable object within which an object such as a Control is returned, is this managed by Unigine or C++? Do I need to ensure proper deallocation of Variable objects?

Link to comment

Documentation will be soon, please stand by.

More correct way is using Unigine::ControlsPtr smart pointer, but you have to cast this pointer using Unigine::VariableToType method (this method is safe casts):

 


Unigine::ControlsPtr controls;

Variable controlsVariable = engine->runWorld("Character::getControls");
controls = VariableToType<ControlsPtr>(controlsVariable).value;

Please see the attached.

World.h

  • Like 1
Link to comment
  • 2 weeks later...
×
×
  • Create New...