0

I have some code that used to work for many years and even now in specific cases it works but in other cases I just cannot understand why it fails.

The following code is part of a Client class that uses a System.Net.Sockets.Socket for communication:

protected ConcurrentQueue<byte[]> ReadQueue { get; } = new ConcurrentQueue<byte[]>();

private void ReadTimer_Tick(object sender, EventArgs e) {
    ReadTimer.Stop();

    try
    {
        while (ReadQueue.Count > 0 && !IsDisposing)
        {
            try
            {
                if (this.ReadQueue.TryDequeue(out var data))
                {
                    [...]
                }
            }
            catch (Exception ex)
            {
                [...]
            }
        }

    }
    catch (Exception)
    {
        [...]
    }
    finally
    {
        if (IsConnected && !IsDisposing) ReadTimer.Start();
    }
}

protected void EnqueueData(IEnumerable<byte> data)
{
    ReadQueue.Enqueue(data.ToArray());
}

The ReadTimer ticks every millisecond if it is not stopped in order to process data from the ConcurrentQueue.

There are two uses of the code:

First case

I open a connection to a Socket port. After the connection is established I call the Socket.BeginReceive method of the Socket.

Second case

I listen to a Socket port and call the Socket.BeginAccept method. Within the ´callback´ method of BeginAccept I also call the BeginReceive method of the Socket.

In both cases the same method is called:

private void StartReceiving(SocketAnswerBuffer state)
{
    try
    {
        Status = ClientStatus.Receiving;

        _ = state.Socket.BeginReceive(
            state.Buffer, 0,
            state.Buffer.Length,
            SocketFlags.None,
            ReceiveCallback,
            state
        );
    }
    catch (Exception ex)
    {
        [...]
    }
}

So in both cases the ReceiveCallback is used to handle incoming data:

private void OnReceive(IAsyncResult result)
{
    if (result.AsyncState is SocketAnswerBuffer state)
    {
        try
        {
            var size = state.Socket.EndReceive(result);

            if (size > 0)
            {
                var data = state.Buffer.Take(size).ToArray();
                EnqueueData(data);
            }
        }
        catch (Exception ex)
        {
            [...]
        }
        finally
        {
            Status = ClientStatus.Connected;
            if (state != null && state.Socket.Connected)
                StartReceiving(state);
        }
    }
}

In both cases the EnqueueData method is called.

In the first case everything works. When the ReadTimer ticks ReadQueue.Count is more than 0 and the loop handles all data collected so far and processes it.

In the second case EnqueueData is also called and enqueues data to the ReadQueue. But when the ReadTimer ticks ReadQueue.Count is 0 and nothing works.

What I really cannot understand is that debugging the code shows that ReadQueue.Count is larger than 0 on EnqueueData and the ReadQueue even grows but in ReadTimer_Tick the ReadQueue remains empty ... I neither clear nor redeclare ReadQueue and ReadTimer_Tick is the only method in code that tries to dequeue the data from ReadQueue.

Oliver
  • 1
  • 2
  • Where do you restart the timer after its been stopped? – Matthew Watson Oct 18 '22 at 09:28
  • 1
    The last command of the ReadTimer_Tick restarts the timer if the conditions are correct. – Oliver Oct 18 '22 at 09:30
  • Oh yes, missed that. – Matthew Watson Oct 18 '22 at 09:33
  • This code worked for years for a serial port and it works when I open the connection but it fails if the connection is open from another client to my socket. I have no idea what happens ... – Oliver Oct 18 '22 at 09:35
  • Sorry, I have no ideas other than noting that the documentation for `ConcurrentQueue` states `For determining whether the collection contains any items, use of the IsEmpty property is recommended rather than retrieving the number of items from the Count property and comparing it to 0.` - I don't know *why* it says that, but maybe you could try using `!ReadQueue.IsEmpty` instead of `ReadQueue.Count > 0`. I doubt that will make any difference, though. – Matthew Watson Oct 18 '22 at 09:43
  • Hah, looking at [the implementation](https://source.dot.net/#System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs,f36684fa5c19fdfb) now I see why they recommend using `IsEmpty`. Still don't think it will fix your issue though... but worth a try. – Matthew Watson Oct 18 '22 at 09:45
  • The problem is that if I set a breakpoint to `EnqueueData` in order to check either `Count` or `IsEmpty` both will tell me that `ReadQueue` is not empty. I can even take a look on the data stored in the queue. But when I set a breakpoint within the while loop of `ReadTimer_Tick` it will never stop. If I set it before the while loop it will tell me that `ReadQueue` has no data. PS: I also tried `IsEmpty` and it didnt work. – Oliver Oct 18 '22 at 09:52
  • It's really difficult to diagnose these things when setting breakpoints in this kind of code. Can you try using `Trace.WriteLine(ReadQueue.Count)` or somesuch and look at the output with a debugger attached (or use [DebugView](https://learn.microsoft.com/en-us/sysinternals/downloads/debugview) to watch the debug output). – Matthew Watson Oct 18 '22 at 10:13
  • Thank you for your input. I tried to trace `IsEmpty` like that and although data arrives every 4 seconds all I get is a `True`. And the breakpoint within the while loop never stopped so there was no data ... which is absurd since setting a breakpoint at `EnqueueData` told me that there was plenty of data ... it is just totally weird. – Oliver Oct 18 '22 at 11:18
  • You haven't accidentally got two queues or something mad like that, have you? – Matthew Watson Oct 18 '22 at 14:05
  • I only defined one queue, but for some reason I can't explain it was filled in the `EnqueueData` method and empty in the `ReadTimer_Tick`. I found an answer and added it to the question. Thank you for trying to help me @MatthewWatson. – Oliver Oct 18 '22 at 14:19

1 Answers1

0

Somehow creating a new class that includes the Timer, the ConcurrentQueue and the method that proceeds the data and using this class inside the class with the Socket forced the ConcurrentQueue to be in sync with the Timer and the method.

Oliver
  • 1
  • 2