0

I wonder how it is possible that this strcture throws an IOException on the Open line saying that this resource is already used but, at the same time, the IsOpen returns false. The close is executed in another thread.

I'm using .NET 6 and the nugget System.IO.Ports (6.0.0)

if(!serialPort.IsOpen)
{
    serialPort.Open();
}

serialPort.Close(); -> This close is executed in another thread

if(!serialPort.IsOpen)
{
    serialPort.Open(); //Throws IOException
}

The exception is:

Exception in open port: System.IO.IOException: Ya se está usando el recurso solicitado. : 'COM9'
   at System.IO.Ports.SerialStream..ctor(String portName, Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Int32 readTimeout, Int32 writeTimeout, Handshake handshake, Boolean dtrEnable, Boolean rtsEnable, Boolean discardNull, Byte parityReplace)
   at System.IO.Ports.SerialPort.Open()
   at ExtDeveloper.EmvConnector.AttemptReconnection(CancellationToken token)

This is the real code


//This task is responsible for calling every time the FSM. This task is in a separated class.

public async Task FSM_EMV(CancellationToken token)
{
    bool stopFsm = false;
    if (EmvSpecificConnector is not null)
    {
        try
        {
            while (!token.IsCancellationRequested && !stopFsm)
            {
                if (EmvSpecificConnector.GetInitEmvStatus())
                {
                    stopFsm = await EmvSpecificConnector.SpecificFSM(token);        //stopFSM is a flag that finalizes the task. But not used in our case
                }
                else
                {
                    Console.WriteLine("Wait to reconnect");
                    await Task.Delay(10000, token);
                    Console.WriteLine("Attempting to reconnect");
                    EmvSpecificConnector.AttemptReconnection(token);
                }
            }
            token.ThrowIfCancellationRequested();
        }
        catch (OperationCanceledException e)
        {
            Console.WriteLine("FSM CANCELED: " + e);
        }
        catch (Exception e)
        {
            Console.WriteLine("FSM unexpected exception: " + e);
        }
    }
}

--------------------------------------------------------------------------------------------------------------------------------------
//This functions are executed from a dll which is included and instanced during runtime in the previous class as a property

public void AttemptReconnection(CancellationToken token)
{
    try
    {
        serialPort = new SerialPort(ConfigParam.SerialPort, int.Parse(ConfigParam.Baudrate), Parity.None, 8, StopBits.One)
        {
            Handshake = Handshake.None,
            ReadTimeout = int.Parse(ConfigParam.ReadTimeout),
            WriteTimeout = int.Parse(ConfigParam.WriteTimeout),
        };

        if(!serialPort.IsOpen)
        {
            serialPort.Open();  //This throws exception the second time it comes here. The first open works well, but after a close, it throws exception.
        }
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception in open port: " + e);
        InitOk = false;
    }
}

public async Task<bool> SpecificFSM(CancellationToken token)
{
    try
    {
        switch (FsmVars.NextState)
        {
            case States.WAIT_ORDER: //Wait order from server
                
                //In case we need to listen from different cancellation tokens, we need this list of tokens.
                //https://learn.microsoft.com/en-us/dotnet/standard/threading/how-to-listen-for-multiple-cancellation-requests
                using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(token, ServerTxToken.Token))
                {
                    try
                    {
                        FsmVars.RqToEmv = await EmvMsgTools.WaitOrderFromServer(Ch_LinceToEmv, linkedCts);  //Wait for a server order,
                                                                                                            //but break if the serial device disconnects.
                        if(FsmVars.RqToEmv is not null)
                        {
                            FsmVars.NextState = States.READ_ORDERS;
                        }
                        else
                        {
                            if(token.IsCancellationRequested)
                            {
                                return true;        //Stop FSM
                            }
                            else if(ServerTxToken.Token.IsCancellationRequested)
                            {
                                serialPort.Close();         //<- Here I close the serial port, if the  
                                serialPort.Dispose();
                                serialPort = null;
                                return false;       //Serial port has been closed and we need to reconnect. break the FSM but do not finish it.
                            }
                        }                                   
                    }
                    catch (OperationCanceledException)
                    {
                    }
                }
                break;

            case States.READ_ORDER:

                break;
        }
    }
    catch(Exception e) 
    {
        Console.WriteLine(e.Message);
    }
}
LexFerrinson
  • 163
  • 7
  • How much time has passed between the Close() and Open()? You should not open the port too soon after closing it, as it takes time to be closed internally. This is also stated in the documentation. – jason.kaisersmith Sep 22 '22 at 08:45
  • @jason.kaisersmith there is a wait of 10 seconds between the close and the open – LexFerrinson Sep 22 '22 at 08:46
  • I have the feeling that something went wrong during closing of the port. please provide the necessary context of the calling site of the Close method. are you catching exceptions there? Please show us the other thread situation – Mong Zhu Sep 22 '22 at 09:27
  • [Documentation says](https://learn.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.open?view=dotnet-plat-ext-6.0#remarks) "The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly." To be honest, I would create a new object anyway rather than reusing the same object. – Charlieface Sep 22 '22 at 09:36
  • 3
    "AttemptReconnection" is a good hint, never do that with a serial port. Closing and opening again never solves a problem, it only adds more problems. Including allowing another process to open the port, preventing you from re-opening it. – Hans Passant Sep 22 '22 at 09:39
  • @MongZhu Done. I think the same. Moreover, if I close the serial port and then I add a breakpoint in the `return false` and I try to access through any serial port console it gives the same error. – LexFerrinson Sep 22 '22 at 09:48
  • @HansPassant Then, if i detect the serial device has disconnected, should I just keep the port open? If I do so, when I connect the serial device again, it tells me that serialPort.IsOpen is false, and then I'm obligated to do serialPort.Open which throws exception. – LexFerrinson Sep 22 '22 at 09:52
  • @Charlieface I've waited for a minute and no changes. I also have called close dispose and null. And then in the attemptReconnecting I have created a new serialPort object and has the same behaviour throwing exception in the .Open – LexFerrinson Sep 22 '22 at 09:53
  • Serial ports have no notion of connection state. They sit at the very bottom of the OSI network model, they implement the physical layer. That's why SerialPort has nothing remotely similar to, say, the Socket.Connected property. So "I detect the serial device has disconnected" is not something you can actually reliably do. Pretending that you can anyway gives you exactly the kind of problem you're having now. – Hans Passant Sep 22 '22 at 10:06
  • @HansPassant well, what I'm detecting is a ReadAsync of the basestream of the serial port. If this await readasync throws an exception means that the base stream is no longer able to receive data. Do you suggest keeping the serial port open, but closing and opening the Stream? When I try to close the basestream it gives an exception `BaseStream is only available when the port is open` so I assume the serial port has closed itself cause I have not close it in my test. – LexFerrinson Sep 22 '22 at 10:10
  • Possibly the fact you are closing it on another thread might affect things. The error you are getting is basically that the original underlying port handle is still open. "some amount of time" is not necessarily 1 minute, I don't know how long. – Charlieface Sep 22 '22 at 10:23
  • Never catch an exception you can't handle. You haven't said enough about how you got the port into that state, but it is somewhat guessable: https://stackoverflow.com/a/9837330/17034 – Hans Passant Sep 22 '22 at 10:27
  • @HansPassant I've inspected the serial port with a serial monitor. When I try to do the .Open in c# or in a serial terminal the device responds with a STATUS_DEVICE_BUSY in the function IRP_MJ_CREATE. Any idea about that? Why is it busy if I've used the close? – LexFerrinson Sep 22 '22 at 12:01

0 Answers0