CAN: slow event handlers and buffer overflows

My issue is that the internal buffer constantly overflows no matter how large I make it. The code is set up much like the sample code in the TinyCLR example, except that I am using one filter, reading 22 messages at a time, and setting the internal buffer size to any value (I have tried from 22 to 1500 messages).

I did a small test to see how many messages would fill the internal buffer by the time the event handler responded by using sender.ReceivedMessageCount, and found it to be around 130 messages. So no matter how many messages I read at a time, the buffer overflows. Isn’t an event handler supposed to be fast??? Am I correct in assuming that getting a message clears that space in the internal buffer so that another message can be received?

When I say ‘22 messages at a time’ I am using:
sender.GetMessages(msgList, 0, msgList.Length);

My intent is to read 22 messages at a time, because the information repeats every 22 messages.
The CAN bus I am interfacing with is running at 1 Mb/s at near full load - these are things that I cannot change.

I apologize, I am using the FEZ Spider and the CAN module.

CAN is very fast. You need to enable filters on specific messages you need and may need to process data in RLP.

Welcome to the community.

Thanks! I have enabled filters on my specific message, so I will give RLP a try and provide an update when I can.

I decided to not go with RLP since speed is not one of my requirements; I only need to sample the bus and I am not worried about losing messages.


using System;
using System.Collections;
using System.Threading;
using System.IO.Ports;
using System.Text;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using GHIElectronics.NETMF.Hardware;


namespace CANstuff
{
    public partial class Program
    {
        CAN.Message[] msgList;

        int i, j, offset = 0;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            int T1, T2, BRP;
            
            Debug.EnableGCMessages(true);

            BRP = 3;
            T1 = 15;
            T2 = 8;

            // Use channel 1 and initialize module
            CAN can = new CAN(CAN.Channel.Channel_1, (uint)(((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0)), 100);
                     
            // and set filter
            uint[] explicitIDs = new uint[] { 0x5F0 };
            can.SetExplicitFilters(explicitIDs);

            // create a message list of 65 messages
            msgList = new CAN.Message[65];
            for (int i = 0; i < msgList.Length; i++)
                msgList[i] = new CAN.Message();

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            while (true)
            {
                try
                {
                    can.GetMessages(msgList, 0, msgList.Length);
                }
                finally
                {
                    //can.Dispose();
                    Debug.GC(true);
                }
           
                Thread.Sleep(10);

            }
        }
    }
}

The GC doesn’t seem to be releasing msgList ( I believe it is msgList, as this happens when I use: can.GetMessages(msgList, 0, msgList.Length); ), as BINARY_BLOB_HEAD and STACK_FRAME grow uncontrolled (STACK_FRAME grows much faster).

From what I have read, I may need to dispose of the data before using GC (at least that is what I got from reading through the forum and going over “Destructors and Finalizers in Visual C++” on the Visual Studio site), but if I uncomment “can.Dispose();” I get this error:

Exception System.ObjectDisposedException - CLR_E_OBJECT_DISPOSED (1)

#### Message: 
#### GHIElectronics.NETMF.Hardware.CAN::GetMessages [IP: 0000] ####
#### CANstuff.Program::ProgramStarted [IP: 0087] ####
#### CANstuff.Program::Main [IP: 0015] ####

A first chance exception of type ‘System.ObjectDisposedException’ occurred in GHIElectronics.NETMF.Hardware.dll
An unhandled exception of type ‘System.ObjectDisposedException’ occurred in GHIElectronics.NETMF.Hardware.dll

Now I know that this means that I am trying to use a resource that has already been disposed, as I receive this error when I reach can.Message for a second time. So how do I release these resources???

As a side note: I am using the cAN_DW module, and I haven’t seen any documentation of whether I should use its methods or not… For example, I could also use: cAN_DW.InitializeCAN((uint)(((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0)), 100);

Thanks everyone!

Do not add CAN DW to the gadgeteer designer for now. Your code looks fine so I am not sure. Have you tried the example in library reference unchanged http://www.ghielectronics.com/downloads/NETMF/Library%20Documentation/Index.html

I just tried it: the internal buffer fills before anything can be read, so it never makes it to can_ErrorReceivedEvent. The solution here is probably, as you mentioned earlier, RLP. However, I do not want to subscribe to each received message (I am updating a screen, so there is no reason to read every message and thus I don’t need RLP). In fact, I would really prefer that the internal buffer filled, and stay filled until I released some memory, or continuously gets overwritten with new messages. So I come back to my initial problem of freeing resources. That is why I used a ‘while’ loop, because I intend to use threading in the end application.

Remove debug statements and run your can messages slower. I am just curious on the memory growing out of control.

I cannot run the CAN slower. I am interfacing with an already present CAN bus on a vehicle

While I don’t know if this is a factor or not, the golden Gadgeteer rule (that may even trump Gus’ firmware #1 rule) is that you should never block ProgramStarted() from finishing. You need to rip your code out of where you have it and put it elsewhere :slight_smile:

That’s certainly a good idea. I gave it a try:


using System;
using System.Collections;
using System.Threading;
using System.IO.Ports;
using System.Text;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using GHIElectronics.NETMF.Hardware;


namespace CANstuff
{
    public partial class Program
    {
        CAN.Message[] msgList;
        int i;
        CAN can;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            int T1, T2, BRP;
            
            Debug.EnableGCMessages(true);

            BRP = 3;
            T1 = 15;
            T2 = 8;

            // Use channel 1 and initialize module
            CAN can = new CAN(CAN.Channel.Channel_1, (uint)(((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0)), 100);
            //cAN_DW.InitializeCAN((uint)(((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0)), 100);
                     
            // and set filter
            uint[] explicitIDs = new uint[] { 0x5F0 };
            can.SetExplicitFilters(explicitIDs);

            // create a message list of 65 messages
            msgList = new CAN.Message[65];
            for (int i = 0; i < msgList.Length; i++)
                msgList[i] = new CAN.Message();

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            Thread thread1 = new Thread(getCAN);
            thread1.Start();

            Thread.Sleep(10);
        }

        void getCAN()
        {
            try
            {
                can.GetMessages(msgList, 0, msgList.Length);
            }
            finally
            {
                Debug.GC(true);
            }
            Thread.Sleep(10);
        }
    }
}

and received this error message once the code tries to execute can.GetMessage():

Exception System.NullReferenceException - CLR_E_NULL_REFERENCE (4)

#### Message: 
#### CANstuff.Program::getCAN [IP: 0014] ####

A first chance exception of type ‘System.NullReferenceException’ occurred in CANstuff.exe
An unhandled exception of type ‘System.NullReferenceException’ occurred in CANstuff.exe

If I continue, the GC attempts to clean up:
GC: 39msec 1194588 bytes used, 11846460 bytes available
Type 0F (STRING ): 1356 bytes
Type 11 (CLASS ): 16452 bytes
Type 12 (VALUETYPE ): 1476 bytes
Type 13 (SZARRAY ): 8280 bytes
Type 15 (FREEBLOCK ): 11846460 bytes
Type 16 (CACHEDBLOCK ): 72 bytes
Type 17 (ASSEMBLY ): 30408 bytes
Type 18 (WEAKCLASS ): 240 bytes
Type 19 (REFLECTION ): 168 bytes
Type 1B (DELEGATE_HEAD ): 648 bytes
Type 1D (OBJECT_TO_EVENT ): 192 bytes
Type 1E (BINARY_BLOB_HEAD ): 1129212 bytes
Type 1F (THREAD ): 1152 bytes
Type 20 (SUBTHREAD ): 144 bytes
Type 21 (STACK_FRAME ): 1092 bytes
Type 22 (TIMER_HEAD ): 72 bytes
Type 27 (FINALIZER_HEAD ): 96 bytes
Type 31 (IO_PORT ): 108 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 3348 bytes
GC: 39msec 1202616 bytes used, 11838432 bytes available
Type 0F (STRING ): 1356 bytes
Type 11 (CLASS ): 16452 bytes
Type 12 (VALUETYPE ): 1476 bytes
Type 13 (SZARRAY ): 8280 bytes
Type 15 (FREEBLOCK ): 11838432 bytes
Type 17 (ASSEMBLY ): 30408 bytes
Type 18 (WEAKCLASS ): 240 bytes
Type 19 (REFLECTION ): 168 bytes
Type 1B (DELEGATE_HEAD ): 648 bytes
Type 1D (OBJECT_TO_EVENT ): 192 bytes
Type 1E (BINARY_BLOB_HEAD ): 1137312 bytes
Type 1F (THREAD ): 1152 bytes
Type 20 (SUBTHREAD ): 144 bytes
Type 21 (STACK_FRAME ): 1092 bytes
Type 22 (TIMER_HEAD ): 72 bytes
Type 27 (FINALIZER_HEAD ): 96 bytes
Type 31 (IO_PORT ): 108 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 3348 bytes
Uncaught exception
The thread ‘’ (0x4) has exited with code 0 (0x0).
The program ‘[10] Micro Framework application: Managed’ has exited with code 0 (0x0).

It would seem that the CAN is forgotten once ProgramStarted exits.

@ Arrested_Mechanics - I have not looked at your code in detail, so there might be other issues, but the NullReferenceException is because you have two variables with the same name in different scopes. You have a ‘can’ variable which is global and you have another new variable called ‘can’ in the ProgramStarted method.

So locally within ProgramStarted you are initializing the local variable, but when you call out to getCAN, the global variable is in scope and it is not initialized.

Remove the variable declaration from ProgramStarted and only perform the initialization, that should move you forward.

So

CAN can = new CAN(…)

Should be

can = new CAN(…)

Change

CAN can = new CAN(CAN.Channel.Channel_1, (uint)(((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0)), 100);

to

can = new CAN(CAN.Channel.Channel_1, (uint)(((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0)), 100);

Haha, how did I miss that!

Actually, that worked! Except that my free space is still slowly dwindling :frowning: back to square one

What is the type of memory that is increasing then in the GC output?

BINARY_BLOB_HEAD

After calling canGet(), the debugger would end; I threw the function into a while loop so that it would continually run. This is how I know that the memory is being eaten, since I can’t tell if the program ends.

EDIT: What I mean is this: the code that you see above, my second posting of the code, would give me those errors and then end. When I applied @ Architect’s and @ taylorza’s fix, the error disappeared but the program still exited. I would have to guess that since my ProgramStarted() routine ends after a quick sleep, and it was used to initiate thread1, the program ends(?). So I enclosed the contents of getCAN() in a while loop to keep the program running.

Again, thank you for your suggestions! Communities like this rock.

Figured it out!!!


using System;
using System.Collections;
using System.Threading;
using System.IO.Ports;
using System.Text;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;
using GHIElectronics.NETMF.Hardware;


namespace CANstuff
{
    public partial class Program
    {
        int i, j, dummy = 0;
        static int T1 = 15, T2 = 8, BRP = 3;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
                      
            Debug.EnableGCMessages(true);
                    

            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            Thread thread1 = new Thread(getCAN);
            thread1.Start();

            Thread.Sleep(Timeout.Infinite);
        }

        void getCAN()
        {
            while (true)
            {
                setupCAN();
            }
        }

        void setupCAN()
        {

            CAN.Message[] msgList;
            CAN can = new CAN(CAN.Channel.Channel_1, (uint)(((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0)), 100);

             // and set filter
            uint[] explicitIDs = new uint[] { 0x5F0 };
            can.SetExplicitFilters(explicitIDs);

            // create a message list of 65 messages
            msgList = new CAN.Message[65];
            for (int i = 0; i < msgList.Length; i++)
                msgList[i] = new CAN.Message();
           
            Thread.Sleep(10);

            try
            {
                can.GetMessages(msgList, 0, msgList.Length);
            }
            finally
            {
                can.Dispose();
            }

            for (j = 0; j < msgList.Length; j++)       // Ignore this section, I was using it for testing
            {
                for (i = 0; i < 8; i++)
                {
                    if (msgList[j].Data[i] == 0x82)
                    {
                        dummy = 1;
                    }
                }
            }
        }
    }
}

The internal buffer for the CAN is an unmanaged resource, and is therefore not cleaned up by the GC. Since the CAN class is defined as an IDisposable, the Dispose method needs to be used to clean up. Unfortunately, this results in the entire CAN protocol closing, probably because open serial ports are never cleaned.

To solve this, I closed the CAN with the Dispose method when I finished sampling the bus, and reopened it when I needed to. However, IF I were required to constantly monitor the bus in real-time, this method may be inefficient, as I would assume that opening and closing a protocol eats up necessary time. I would be incredibly interested to hear about how someone would go about this, but @ Gus is probably right: since my bus is screaming fast, RLP may be the answer.

your thread.sleep(timeout.infinite) is NOT what you want in a Gadgeteer application. You’re still blocking the app from terminating properly. http://blogs.msdn.com/b/net_gadgeteer/archive/2011/12/19/why-not-while-true.aspx is the bible here. Kerry is also a poster here so she may see this (actually I’ll ping her and make sure she does :slight_smile: ) The key is that your app may not be using Gadgeteer so perhaps you should move back out of the gadgeteer paradigm ?

The key with handling a serial input like CAN is to use some form of continuous loop to collect data and hand it off for processing (often that’s a datarecieved handler that dequeues content and a processing loop, or it’s a while(1) loop that looks for data and processes it; two different approaches to get the same result.)

My view is that you should be looking to “set up” the CAN once, in programstarted(), and then start the thread that processes stuff in programstarted(), and then figure out why your app is ending when there’s a running thread.