0

I am writing usual reader-writer functionality with one main thread that equeues and several threads that dequeues. So, there is a part of the code where I am comparing count of items in my ConcurrentQueue with some integer number, let's call it "maxSize". Despite the fact that .Count returns 1 and maxSize is 10, queue.Count >= maxSize returns true.

I tried to debug with breakpoints, set only one dequeue thread and even pause it. That happened right after main thread enqueued and after several lines of code this comparing returns the result that says 1 >= 10. I am sure the main thread puts only 1 item at this moment, I am sure no Dequeue() was called. Also, I've tried to double-check with locking but sometimes it does not help. I am wondering that there could be some magic that does not allow me to compare values properly in a way I do it, because when I see in a debugger that 1 >= 10 is true, I'm torn apart.


int maxSize = 10;
Timer timer;

ctor(int interval)
{
    queue = new ConcurrentQueue<HttpSessionState>();
    timer = new Timer(TimeSpan.FromSeconds(interval).TotalMilliseconds);
    timer.Elapsed += (sender, args) => PulseIfAvailableForProcessing(true);
}

void Process()
{
    queue.Enqueue(obj);
    // interval here is huge, several minutes
    timer.Start();
    PulseIfAvailableForProcessing(false);
}

bool PulseIfAvailableForProcessing(bool isTimeout)
{
    if (isTimeout)
    {
        ...
    }
    else
    {
        // here 1 >= 10 gives true
        if (queue.Count >= maxSize)
        {
            lock (_dataLock)
            {
                // here in debug queue.Count is still 1, however 1 >= 10 returns false
                if (queue.Count >= maxSize)
                {
                    Pulse();
                }
            }
        }
    }
}

In despair I've added logging and I see that during unit testing the issue is reproducible even inside of a lock statement.

Yngvin
  • 1
  • 1
  • 1
    When something seems impossible, question all assumptions. If `queue.Count >= maxSize` then it's safe to say that the values can't be 1 and 10. There are either more items in the queue, or `maxSize` has changed. The former seems more likely. It would help if we could see more context. There must be more to it than what we can see. Can you post the relevant parts of the class? – Scott Hannen Apr 22 '19 at 17:10
  • I can add that between `queue.Enqueue(obj);` and the first `if` statement I am starting `System.Timers.Timer`, it's callback calls the `if` statements I described. Nothing more. And I tried to put infinite timeout to it - didn't help. I've updated code a little bit in the question. – Yngvin Apr 22 '19 at 17:25
  • We can't do much more to help you. There are 3 possibilities, in order of likeliness: you have 10 or more elements in the queue, there's a bug in `queue.Count` (very unlikely on .net framework, plausible if running on .net core since the ConcurrentQueue has been revamped recently), there's a bug in the JIT (very **very** unlikely). In all three cases, we can't do anything without a repro – Kevin Gosse Apr 22 '19 at 18:37
  • You can start by saving the count in a variable before doing the comparison (`var count = queue.Count; if (count >= maxSize) { ... }`, this way, you can inspect what value was used for the comparison, and we can rule out the JIT bug – Kevin Gosse Apr 22 '19 at 18:38
  • That said, it seems that you have multiple timers (since you start one every time you add an item to the queue). Therefore, it seems reasonable to think that another timer ticks while you're debugging and unblocks the dequeue thread, thus changing the count – Kevin Gosse Apr 22 '19 at 18:42
  • @KevinGosse seems that the debugger shows wrong value for `.Count`. Since I've started using temp variable for the comparing, I can see that `.Count` shows "1" and temp shows "10". That's why `if` returns `true` that `1 >= 10` - because actually it's `10 >= 10`. – Yngvin Apr 23 '19 at 07:21

0 Answers0