20

The System.Threading.ConcurrentQueue.TryDequeue method threw an exception the other day that took me totally by surprise. Here's the stack trace:

System.OverflowException: Negating the minimum value of a twos complement number is invalid.
   at System.Math.AbsHelper(Int32 value)
   at System.Random..ctor(Int32 Seed)
   at System.Threading.Collections.ConcurrentQueue`1.TryDequeueCore(T& result)
   at System.Threading.Collections.ConcurrentQueue`1.TryDequeue(T& result)
   at MyProgram.ThreadProc() in c:\MyProgram\Main.cs:line 118
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

At first I thought the problem was that TryDequeueCore called the Random constructor with a bad value. But further investigation reveals that TryDequeueCore calls the default constructor. It looks to me like the error is in the Random constructor:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       12 (0xc)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       int32 System.Environment::get_TickCount()
  IL_0006:  call       instance void System.Random::.ctor(int32)
  IL_000b:  ret
} // end of method Random::.ctor

As the documentation for the System.Environment.TickCount property says:

The value of this property is derived from the system timer and is stored as a 32-bit signed integer. Consequently, if the system runs continuously, TickCount will increment from zero to Int32..::.MaxValue for approximately 24.9 days, then jump to Int32..::.MinValue, which is a negative number, then increment back to zero during the next 24.9 days.

So, if you call the Random constructor during that one-millisecond period (after the system has been up for int.MaxValue milliseconds), it's going to throw this exception.

Does anybody have a workaround? For my own code, I can make a CreateRandom method that gets the TickCount value and checks it for int.MinValue. But what to do about code that I have no control over?

I hope the RTL team fixes this in .NET 4.0.

Update 2009/07/22: The BCL Team responded to the bug and said that it has been resolved for the next release.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351

1 Answers1

4

try/catch and retry a millisecond later seems just about the only thing you can do until this bug does get fixed.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • Perhaps. But take the case of ConcurrentQueue.TryDequeue. That method isn't supposed to throw exceptions at all. I now have to wrap all calls to TryDequeue in a try/catch block. And what about code that I don't know uses Random? – Jim Mischel Jul 18 '09 at 20:05
  • As long as deep down in the system libraries there's a bug that causes exceptions to be thrown when one should never be, what else can you do with other code outside of your control, to which you have no sources, and which MIGHT call the system lib in question? Unless you can switch to an open-source stack, where you could fix errors once you find them, you're inevitably at the proprietary code supplier's mercy. try/catch is just about the only thing you can do, and it's definitely not perfect, but, what else? – Alex Martelli Jul 18 '09 at 23:18