TinyCLR 0.4 DataReader Load Generates ArgumentOutOfRangeException

It appears that the new DataReader Load member generates a System.ArgumentOutOfRangeException when called and there is no data in the serial input buffer, in addition (if handled by Try - Catch) to returning 0 (as would be expected). The Windows 10 IoT API documentation equivalent is LoadAsync, which does not apply in this case. Is there additional documentation on the TinyCLR implementation to confirm this behaviour?

I have assumed that the parameter passed is the number of bytes to read, the returned value is the number of bytes actually read. If the number of bytes read is greater than one then use UnconsumedBufferLength to step through.

Below is an example code segment inside a reader thread.


 _reader.InputStreamOptions = InputStreamOptions.Partial;

while (true)
            {
                try
                {
                    data_read_size = _reader.Load(1);
                }
                catch (System.ArgumentOutOfRangeException ex)
                {
                    Debug.WriteLine("data_read_size: " + data_read_size.ToString());
                }

                if (data_read_size > 0)
                {
                    while (_reader.UnconsumedBufferLength > 0)
                    {
                        byte data = _reader.ReadByte();
                        // Process received data
                    }
                }
            }

@ Drovers_Dog - What’s the callstack for the exception? Can you post a minimal but complete example that shows the issue?

In general our API should match exactly the UWP API in terms of behavior. Since TinyCLR does not currently support generics, we cannot have Async, so any async methods will be changed to synchronous.

My apologies for the delayed reply. This is a weekend project.
The following code will generate the exception. It is run on a FEZ Panda III with jumpers between COM1 and COM2.

I have narrowed down the key sequence. If the main thread is allowed to finish (loop commented out as indicated in the code) then no exception is thrown. If the main thread doesn’t finish then the exception is thrown.


using GHIElectronics.TinyCLR.Devices.SerialCommunication;
using GHIElectronics.TinyCLR.Storage.Streams;
using System.Diagnostics;
using System.Threading;

namespace SimpleSerial
{
    internal class Program
    {
        private static void Main()
        {
            Thread ReadThread = new Thread(() => ReaderProcess());
            ReadThread.Start();

            Thread TestStreamThread = new Thread(() => TestWriter());
            TestStreamThread.Start();

            // With the next line of code in place the exception is thrown.
            // When it is commented out the exception is not thrown.
            while (true) { }

            Debug.WriteLine("Main finished.");
        }

        private static void ReaderProcess()
        {
            SerialDevice _inPort = SerialDevice.FromId("COM1");
            _inPort.BaudRate = 57600;
            _inPort.DataBits = 8;
            _inPort.Handshake = SerialHandshake.None;
            _inPort.Parity = SerialParity.None;
            _inPort.StopBits = SerialStopBitCount.One;
            DataReader _reader = new DataReader(_inPort.InputStream)
            {
                InputStreamOptions = InputStreamOptions.Partial
            };
            uint data_read_size = 0;

            while (true)
            {
                //try
                //{
                data_read_size = _reader.Load(1);
                //}
                //catch (System.ArgumentOutOfRangeException ex)
                //{
                //    Debug.WriteLine("data_read_size: " + data_read_size.ToString());
                //    if (data_read_size == 0)
                //        Debug.WriteLine("No data to read.");
                //}

                if (data_read_size > 0)
                {
                    while (_reader.UnconsumedBufferLength > 0)
                    {
                        byte data = _reader.ReadByte();
                        Debug.WriteLine("Data read: " + data.ToString());
                    }
                }
                else
                {
                    Debug.WriteLine("No data to read.");
                }
            }
        }

        private static void TestWriter()
        {
            SerialDevice _outPort = SerialDevice.FromId("COM2");
            _outPort.BaudRate = 57600;
            _outPort.DataBits = 8;
            _outPort.Handshake = SerialHandshake.None;
            _outPort.Parity = SerialParity.None;
            _outPort.StopBits = SerialStopBitCount.One;
            DataWriter _writer = new DataWriter(_outPort.OutputStream);

            byte data = new byte();
            data = 65; // A

            for (int i = 0; i < 10; i++)
            {
                _writer.WriteByte(data);
                _writer.Store();
                Debug.WriteLine("Data write: " + data.ToString());
            }

            Debug.WriteLine("Test Writer Finished.");
        }
    }
}

The exception data is:


 #### Exception System.ArgumentOutOfRangeException - CLR_E_OUT_OF_RANGE (3) ####
    #### Message: 
    #### GHIElectronics.TinyCLR.Devices.SerialCommunication.SerialDevice+Stream::NativeRead [IP: 0000] ####
    #### GHIElectronics.TinyCLR.Devices.SerialCommunication.SerialDevice+Stream::Read [IP: 0097] ####
    #### GHIElectronics.TinyCLR.Storage.Streams.DataReader::Load [IP: 0069] ####
    #### SimpleSerial.Program::ReaderProcess [IP: 0042] ####
    #### SimpleSerial.Program+<>c::<Main>b__0_0 [IP: 0003] ####
Exception thrown: 'System.ArgumentOutOfRangeException' in GHIElectronics.TinyCLR.Devices.dll

@ Drovers_Dog - try replacing the endless loop in main with a sleep.

Yes… Thread.Sleep(-1); is very different from while(true);

Using Thread.Sleep(-1) instead of while(true) -> No exception thrown.

Investigating further to see what happens if the main thread had additional code running along with the other threads. It appears that if the main thread is “heavily” loaded then the exception is thrown. If Thread.Sleep(0) is added within the main thread at places to yield to the other threads the results are mixed as to whether the exception is thrown or not.

Thank you for your assistance.

2 Likes

@ Drovers_Dog - Are you saying that when executing code in the main thread, with sleeps, you are still getting occasional exceptions?

While running an endless loop in the main thread is not good practice, it should not result in a serial exception unless the serial interrupts are being masked or the main thread time slice is too long, and serial buffers or hardware are overflowing, and the resultant error is not being handled properly by the runtime.

1 Like

@ Drovers_Dog - You shouldn’t execute your program within the main thread at all. Always start a new thread and exit the main as soon as possible. There is a post somewhere around here about this issue; if I find it I’ll link it.

1 Like

Thank you Mike and Mr. John Smith.

@ Mike - Yes, additional code within the main thread still causes the exception to be thrown under certain conditions. I could not find a pattern other to relate it to how heavily the thread was loaded.

@ Mr. John Smith - I would appreciate reading the link if you come across it to improve my understanding.

To conclude. I implemented a similar code structure under TinyCLR 0.3 Serial API and had no issues. I am not as familiar with the TinyCLR 0.4 Serial API and wasn’t sure if 1) my understanding was correct, 2) there was an underlying implementation issue, or 3) my code structure could be improved.

As always, learning a lot from the Forum community. Thank you.

You can run code in Main, you just have to make sure you Thread.Sleep() it so that it yields to other threads.

All of my programmes run the GUI in Main without any issues. I Thread.Sleep(250) at the end of the loop.

If he does that, the 2 threads he created will be destroyed.


Thread ReadThread = new Thread(() => ReaderProcess());
ReadThread.Start();

Thread TestStreamThread = new Thread(() => TestWriter());
TestStreamThread.Start();

I think you may be referring to ProgramStarted if you use Gadgeteer? Certainly with that you must exit when done.

@ Dave McLaughlin - Then he’d have to move those thread variables outside of the main an implement them as static.

EDIT: static Thread worker = null;


public class Program {
        static GHI.Usb.Host.Keyboard keyboard = null;
        static L6470Chain stepperChain = null;
        static Thread worker = null;
        static OutputPort led = null;
        public static void Main() {
            GHI.Usb.Host.Controller.KeyboardConnected += Controller_KeyboardConnected;
            GHI.Usb.Host.Controller.Start();
            led = new OutputPort((Cpu.Pin)FEZCerbuino.Pin.Digital.LED1, true);
            worker = new Thread(new ThreadStart(WorkerThread));
            worker.Start();
        }

        public static void WorkerThread() {

Hi Guys,

I have recently been encountering the same issue and was wondering if there is a solution?

for a start, this is for version 0.4. Please use the latest beta, and open a new thread (with the correct version in the title) if you still have an issue

I was not aware of this. Are you saying I should take everything done in my main loop… which is a lot in my app… and just do it in a new thread? In all cases?

Multiple threads if it’s a lot. Also, ensure the main loop does not exit by calling thread.sleep(Timeout.infinite) as the last line. This is the better practise because not everyone writes their programs “properly”.

I’d say that generally, you want a thread (any thread) that never terminates. You either do that in your main loop with an appropriate thread, or you do that in a separate thread. But you can see the code that bought that comment was very bad:
while (true) { }
Which may not allow the processor to release and give cycles to whatever else the app does. So long as you’re doing actual work in your while (true) construct, you’re fine. If you have something dodgy like this, instead put the thread to sleep with thread.sleep(timeout.infinite).

Okay thanks for the reassurance. I use while(true) but it contains most of the work that is done. At the end of the while (true) loop I sleep for 1 millisecond. Everything seems to be working fine.

I have noticed in the past that if I don’t sleep at all in the while (true) loop, then things can get dicey with behavior like running fine in debug mode but not standalone. Or some of my other threads appearing to behave incorrectly. Often times I have crashes without at least 1 ms sleep.

remember that in netmf the standard timeslice size is 20ms, so sleep(1) and sleep(20) are not likely to be that different, unless all your threads are very frugal and release the processor as soon as you can. But absolutely, if you do not release the processor in a thread then you will get issues - you should always code to allow for that.

But lets not discuss this in an old thread that is unrelated!

1 Like