Sign in to follow this  
photo

Garbage Collected Delegate

Recommended Posts

Hi,

We're getting an error on a garbage collected delegate in our UI. The UI is dynamically created by our UI manager on startup and we hold a copy of all the objects within the UI manager to stop them being garbage collected.

In this case, it's on a WidgetSprite that we've added callbacks for mouse enter and leave and click to. If after about a minute, you move the mouse over the object, you will get the error:

A callback was made on a garbage collected delegate of type 'UnigineSharp_double_x64!Unigine.Widget+Callback0::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
 

As we are keeping copies of all of the objects, we should be keeping the objects alive and prevent them from being garbage collected. Is there something obvious we're doing wrong, or is there a recommended way of handling UI's within C#.

Thanks,

Robert

Share this post


Link to post

I think I've solved this in our widget wrapper class: What we had was this:

public void setCallbacks()
{
	sprite.setCallback0(Gui.CLICKED, OnMouseClicked);
}

void OnMouseClicked()
{
	// Handle event
		'
		'
		'
}

And what we need to do is this:

private Widget.Callback0 mouseClicked;

public void setCallbacks()
{
	mouseClicked = OnMouseClicked;
	sprite.setCallback0(Gui.CLICKED, mouseClicked);
}

void OnMouseClicked()
{
	// Handle event
		'
		'
		'
}

I think we'd have expected the wrapper to keep hold of the delegate.

Thanks,

Robert

 

 

Share this post


Link to post

Hi Robert,

You've found a valid workaround. Keeping a reference to a delegate will certainly prevent it from destroying.

Still, it's the responsibility of API to keep it alive.
I'll check what's wrong.

Share this post


Link to post

Sorry, but I can't reproduce.

There is a code for each callback, that pins delegate instance.
It prevents delegates not only from destroying but also from moving around a memory (using GCHandle).
It works fine with my tests.

It would help if you provided us with a sample for reproducing.
You can force garbage collection each engine frame if you insert the following code into the WorldLogic.update():

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Share this post


Link to post

Hello,

sorry for reviving an old thread. But I had an similar issue with custom console commands in Unigine 2.10 with a dotnetcore project.

Invoking any custom console command except the last one added throws an System.ExecutionEngineException exception, it seems (The stacktrace goes into native code Unigine_x64.dll).

I'm just using this thread because Robert's workaround is also working in this case.

Its quite easy to reproduce

1. Create a 64 bit dotnet core project in Unigine 2.10

2. Modify the world logic:

		public override bool Init()
		{
			// Write here code to be called on world initialization: initialize resources for your world scene during the world start.

			Unigine.Console.AddCommand("my_console_command", "my_console_command", my_console_command);
			Unigine.Console.AddCommand("my_console_command1", "my_console_command1", my_console_command1);

            // This line is needed in order to reproduce the crash consistently
			Engine.GCMode = Engine.GCMODE.EVERY_FRAME;

			return true;
		}

		private void my_console_command(int argc, string[] argv)
		{
			Log.Message("my_console_command(): called\n");
			for (int i = 0; i < argc; i++)
				Log.Message("{0}: {1}\n", i, argv[i]);
		}

		private void my_console_command1(int argc, string[] argv)
		{
			Log.Message("my_console_command1(): called\n");
			for (int i = 0; i < argc; i++)
				Log.Message("{0}: {1}\n", i, argv[i]);
		}

3. load the world and run on the console my_console_command.

Cheers

Helmut

 

Share this post


Link to post

Yep, indeed it's a bug. More likely fix will be available in 2.11 release.

Thanks for the repro!

Share this post


Link to post
Sign in to follow this