[Top][All Lists]
[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();
}
}
}
- [Pnet-developers] static constructor bug,
Tim Nichols <=