dotgnu-pnet
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Pnet-developers] static constructor bug


From: Tim Nichols
Subject: [Pnet-developers] static constructor bug
Date: Wed, 01 Feb 2006 09:54:10 -0800
User-agent: Thunderbird 1.5 (Windows/20051201)

Hi -

The code snippet that follows demonstrates a bug in static constructors. The problem is that when accessing a static field, there is only a check if the static constructor has been run, not if it has completed. So it is possible for a thread to access a static member before the static constructor has finished running in another thread.

I hacked in a fix, but it seems too ugly to be generally useful - it involved a new opcode (CCTOR_DONE) that checks if a static constructor has completed, and a per class mutex that is locked in CCTOR_ONCE and unlocked in CCTOR_DONE. This mutex has to be recursive since a single thread can re-enter a static constructor more than once.

If someone has a better fix, please let me know. I'm playing around with the 0.7.2 code base.

In the code below, Mono and MSFT .Net give the correct output, but pnet throws a null reference exception because one of the threads is accessing a static member before the static constructor has initialized it. Here's the correct output (Mono and .Net):

InitValue: 1, 0
InitValue: 2, 1000
InitValue: 4, 5000
InitValue: 1, 0
InitValue: 2, 1000
InitValue: 4, 5000
InitValue: 1, 0
InitValue: 2, 1000
InitValue: 4, 5000
Sums in FirstThread: 7, 7, 7
Sums in SecondThread: 7, 7, 7
Sums in MainThread: 7, 7, 7

Here's the output from pnet:

InitValue: 1, 0
InitValue: 2, 1000
Uncaught exception: System.NullReferenceException: The value 'null' was found where an instance of an object was required
       at TestStatic.TestOne.get_Sum()
       at TestStatic.Class1.SecondThread()

InitValue: 4, 5000
Uncaught exception: System.NullReferenceException: The value 'null' was found where an instance of an object was required
       at TestStatic.TestOne.get_Sum()
       at TestStatic.Class1.Main(String[])

InitValue: 1, 0
InitValue: 2, 1000
InitValue: 4, 5000
InitValue: 1, 0
InitValue: 2, 1000
InitValue: 4, 5000
Sums in FirstThread: 7, 7, 7

(This app also demonstrates how often Is*Constructor is called and why having currentMethod not set results in superfluous invocations of CVM_OUT_PTR(COP_CALL, cctor); in cvmc_call.c).

Best,

- Tim

using System;
using System.Threading;

namespace TestStatic
{
   class IntValue
   {
       int value = 0;
       public IntValue(int value, int delay)
       {
           Console.WriteLine("InitValue: {0}, {1}", value, delay);
           Thread.Sleep(delay);
           this.value = value;
       }
       public int Value { get { return value; } }
   }

   class TestOne
   {
       static readonly IntValue fast = new IntValue(1, 0);
       static readonly IntValue medium = new IntValue(2, 1000);
       static readonly IntValue slow = new IntValue(4, 5000);

static public int Sum { get { return fast.Value + medium.Value + slow.Value; } }
   }


   class TestTwo
   {
       static readonly IntValue fast = new IntValue(1, 0);
       static readonly IntValue medium = new IntValue(2, 1000);
       static readonly IntValue slow = new IntValue(4, 5000);
       static TestTwo()
       {
       }
static public int Sum { get { return fast.Value + medium.Value + slow.Value; } }
   }

   class TestThree
   {
       static readonly IntValue fast;
       static readonly IntValue medium;
       static readonly IntValue slow;
       static TestThree()
       {
           fast = new IntValue(1, 0);
           medium = new IntValue(2, 1000);
           slow = new IntValue(4, 5000);
       }
static public int Sum { get { return fast.Value + medium.Value + slow.Value; } }
   }

   class Class1
   {
       static void FirstThread()
       {
Console.WriteLine("Sums in FirstThread: {0}, {1}, {2}", TestOne.Sum, TestTwo.Sum, TestThree.Sum);
       }

       static void SecondThread()
       {
Console.WriteLine("Sums in SecondThread: {0}, {1}, {2}", TestOne.Sum, TestTwo.Sum, TestThree.Sum);
       }

       static void Main(string[] args)
       {
           Thread thread1 = new Thread(new ThreadStart(FirstThread));
           Thread thread2 = new Thread(new ThreadStart(SecondThread));
           thread1.Start();
           Thread.Sleep(500);
           thread2.Start();
           Thread.Sleep(1000);
Console.WriteLine("Sums in MainThread: {0}, {1}, {2}", TestOne.Sum, TestTwo.Sum, TestThree.Sum);
           thread1.Join();
           thread2.Join();
       }
   }
}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]