robert.mcmillan Posted July 17, 2018 Share Posted July 17, 2018 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 Link to comment
robert.mcmillan Posted July 18, 2018 Author Share Posted July 18, 2018 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 Link to comment
ded Posted July 18, 2018 Share Posted July 18, 2018 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. Link to comment
ded Posted July 18, 2018 Share Posted July 18, 2018 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(); Link to comment
helmut.bressler Posted January 10, 2020 Share Posted January 10, 2020 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 Link to comment
silent Posted January 10, 2020 Share Posted January 10, 2020 Yep, indeed it's a bug. More likely fix will be available in 2.11 release. Thanks for the repro! How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN Link to comment
brunoasdfgh Posted July 24, 2020 Share Posted July 24, 2020 The error is still hapening. I am trying to do this: private float time = 0; private void Init() { node.ObjectBodyRigid.AddContactCallback(onContactWithGround); } private void Update(){ time += Game.IFps; if(time > 10 && node.ObjectBodyRigid.Immovable){ node.WorldPosition = new vec3(0,0,10); node.ObjectBodyRigid.Immovable = false; } } public void onContactWithGround(Body body, int num){ respawnIn10Seconds(); } public void respawnIn10Seconds(){ time = 0; node.ObjectBodyRigid.Immovable = true; } And after a while, a get the crash and the message: FailFast: A callback was made on a garbage collected delegate of type 'UnigineSharpCore_x64d!Unigine.Body+ContactDelegate::Invoke'. Link to comment
silent Posted July 27, 2020 Share Posted July 27, 2020 brunoasdfgh Could you please provide a minimal test scene with reproduction (data and source directories contents will be enough). Thanks! How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN Link to comment
denis.totmin Posted July 31, 2020 Share Posted July 31, 2020 I am also facing this error. Unfortunately, it is not permanent, sometimes everything works correctly. I noticed a pattern. The error occurs more often if you give the GC a lot of work Link to comment
david.cambre Posted August 2, 2020 Share Posted August 2, 2020 (edited) Hi, I am new to Unigene and have experiernced this in verion 2.11 and verrsion 2.12 when using body collision callbacks. The above mentiond workaround seems to work. @silentI send u my testproject for debugging. When using line 67 Process terminated. A callback was made on a garbage collected delegate of type 'UnigineSharpCore_x64d!Unigine.Body+ContactEnterDelegate::Invoke'. at Unigine.Engine.Engine_main(IntPtr, System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef) at Unigine.Engine.Engine_main(IntPtr, System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef) at Unigine.Engine.Main(Unigine.SystemLogic, Unigine.WorldLogic, Unigine.EditorLogic) at UnigineApp.UnigineApp.Main(System.String[]) When using line 69 and 70 in stead of 67 is al seams to work. public class fractures_systems : Component { //V2.12 private Body.ContactEnterDelegate cb; .... etc private void Init() { // Break the mesch in scatter peases on init if(fracture_type == frac_type.Scatter_on_init) { BodyFracture fracture = null; ObjectMeshDynamic m = node as ObjectMeshDynamic; fracture = new BodyFracture(m); fracture.Material = fracture_material.Name; fracture.CreateShatterPieces(cracks); } // List to simplify if statement List<frac_type> fracture_types_for_contactcallback = new List<frac_type>{ frac_type.Scatter_on_contact, frac_type.crack_on_contact, frac_type.slice_on_contact }; if(fracture_types_for_contactcallback.Contains(fracture_type)) //if(fracture_type == frac_type.Scatter_on_contact || fracture_type == frac_type.crack_on_contact ) { //V2.11 //node.ObjectBody.AddContactCallback(ContactCallback); //V2.12 ###### garbage collected delegate ERROR #### node.ObjectBody.AddContactEnterCallback(ContactCallback); //V2.12 //cb = ContactCallback; //### FUNCTIONAL //node.ObjectBody.AddContactEnterCallback(cb); //### FUNCTIONAL } .... etc } Edited August 2, 2020 by david.cambre Link to comment
silent Posted August 3, 2020 Share Posted August 3, 2020 Hi David, Thank you for the test scene. Will check what's going on here. How to submit a good bug report --- FTP server for test scenes and user uploads: ftp://files.unigine.com user: upload password: 6xYkd6vLYWjpW6SN Link to comment
alexander Posted August 3, 2020 Share Posted August 3, 2020 Hi David, I've found a workaround for your case. Please change this: node.ObjectBody.AddContactEnterCallback(ContactCallback); To this: // ... private Body b; private void Init() { // ... b = node.ObjectBody; // save reference to the "ObjectBody" b.AddContactEnterCallback(ContactCallback); // ... } We will try to fix it in next release. Best regards, Alexander Link to comment
david.cambre Posted August 3, 2020 Share Posted August 3, 2020 Thanks Alexander, I was stuck for three days until i read this post. So after silent's post i wanted to make sure you had a good reproducible bug. On 7/27/2020 at 5:48 AM, silent said: brunoasdfgh Could you please provide a minimal test scene with reproduction (data and source directories contents will be enough). Thanks! Link to comment
Recommended Posts