What is the best way to prevent memory being corrupted when calling from c# ?

Apr 26, 2013 at 9:13 AM
Edited Apr 30, 2013 at 7:51 AM
I am getting a momery corruption error when I repeatedly call a procedure in R using the termstrc package. After around 16 calls I get a corrupt memory error or one related to garbage collection:

I create the REngine in the usual way:

REngine.SetDllDirectory(@"C:\Program Files\R\R-2.15.1\bin\i386");
engine = REngine.CreateInstance("RDotNet", new[] { "-q" });

I keep this instance for all future calls as I have found that it is not possible to dispose of this engine and create another instance.

I think the solution is related to using keepalive and managing the interaction between c# and R.NET but I am unsure what best practice is, can anyone provide an example of how this would be done ? Thanks, really appreciated as this makes R.NET unusable for me at present and I was loving it until I hit this issue !

More details on the error as requested:

CallbackOnCollectedDelegate was detected
Message: A callback was made on a garbage collected delegate of type 'R.NET!RDotNet.Internals.blah3::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.
Developer
Apr 29, 2013 at 12:28 AM
Hi,

Can you be more specific about the error messages and stack traces you are getting?

Regards
Apr 30, 2013 at 7:53 AM
I have added this message below, this was all I got. I sometimes have had a memory corruption error as well. Thanks for your help !

CallbackOnCollectedDelegate was detected
Message: A callback was made on a garbage collected delegate of type 'R.NET!RDotNet.Internals.blah3::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.



Developer
May 24, 2013 at 11:05 PM
Thank you;
Apologies for the delay in the reply; it has been a difficult month at work.

This does look like a problem in R.NET itself indeed, and should be reproduced and morphed into a unit test for subsequent versions. Is your code such that you can submit a concise set of C#/R code reproducing the problem?

Thanks
Jul 15, 2013 at 3:09 PM
This will cause this error.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
//using Entropy;
using RDotNet;
using System.Windows;
using System.Threading;

namespace WinEntrophy
{
    public partial class Form1 : Form
    {
        private static bool Started = false;
        private static ArrayList List = new ArrayList();
        private static int[] Counts = new int[256];
        private Thread Graphics;
        private static REngine Rawr;
        private static IntegerVector group1, group2;
        private static ArrayList KeepAlive = new ArrayList();
        private static byte[] Add = new byte[1];

        public Form1()
        {
            InitializeComponent();
        }
        
        private static void RMain()
        {
            for (int lines = 1; lines < 100; lines++){
                //Entropy.Data Source = new Entropy.Data();
                Random Rand = new Random();
                for (int nums = 1; nums < 10; nums++) {
                    //List.Add(Source.Retzme());
                    Rand.NextBytes(Add);
                    List.Add(Add[0]);
                }
            }
            int[] Nums = new int[List.Count];
            int Size = 0;
            foreach (byte Number in List){
                Size++;
                Counts[Convert.ToInt32(Number)] = (Counts[Convert.ToInt32(Number)])+1;
                Nums[Size-1] = Convert.ToInt32(Number);
            }
            Size = 0;
            if (KeepAlive.Count != 0)                                 
            {                                                         
                Rawr.Close();                                         
                Rawr.Dispose();                                       
                GC.Collect();                                         
                GC.WaitForPendingFinalizers();                        
                GC.Collect();                                         
                GC.WaitForPendingFinalizers();                        
                Rawr = REngine.CreateInstance("R", new[] { "-q" });   
            }
            else { 
                GC.KeepAlive(REngine.SetDllDirectory(@"C:\Program Files\R\R-3.0.1\bin\i386"));
                Rawr = REngine.CreateInstance("R", new[] { "-q" }); 
            }
            GC.KeepAlive(Rawr);
            KeepAlive.Add(Rawr);
            group1 = Rawr.CreateIntegerVector(Counts);
            Rawr.SetSymbol("group1", group1);
            group2 = Rawr.CreateIntegerVector(Nums);
            GC.KeepAlive(group1);
            GC.KeepAlive(group2);
            Rawr.SetSymbol("group2", group2);
            Rawr.EagerEvaluate("library(base)");
            Rawr.EagerEvaluate("library(stats)");
            Rawr.EagerEvaluate("x <- group1");
            Rawr.EagerEvaluate("y <- group2");
            Rawr.EagerEvaluate("windows( width=10, height=8, pointsize=8)");
            Rawr.EagerEvaluate("par(yaxp = c( 0, 100, 9))");
            Rawr.EagerEvaluate("par(xaxp = c( 0, 255, 24))");
            Rawr.EagerEvaluate("par(cex = 1.0)");
            //Eng.EagerEvaluate("bins=seq(0,255,by=1.0)");
            //Eng.EagerEvaluate("hist(x:y, breaks=50, col=c(\"blue\"))");
            Rawr.Evaluate("plot(x, type=\"h\", col=c(\"red\"))");
            //Rawr.Close();
            //Rawr.Dispose();
            return;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (Started && Graphics.IsAlive) {
                Graphics.Abort();
            }
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Graphics = new Thread(RMain);
            Graphics.Start();
            Started = true;
        }
    }
}
Developer
Jul 23, 2013 at 6:19 AM
@HoochTHX

Thanks for the sample. I set up a reproduction test and started an issue: https://rdotnet.codeplex.com/workitem/45. Somewhat adapted, notably to (close to) version 1.5 of R.NET. I get one run and the graph output, but on returning from the first thread R aborts with a 'C stack too close to the limit', so not the same issue as reported.

Note that I would be very cautious mixing multi-threading and the R engine. While I think the intent of the code is not concurrent multi-threading, even just re-creating the engine several times may not work.