[Mono-list] Class library developers: locking issues to keep in mind

Serge serge@wildwestsoftware.com
Sun, 28 Oct 2001 13:16:57 +0200


This is a multi-part message in MIME format.

------=_NextPart_000_0050_01C15FB2.D21B33A0
Content-Type: text/plain;
	charset="ISO-8859-1"
Content-Transfer-Encoding: 7bit

The main problem with DCL for static singleton initialization is that caller
may get reference to partially initialized object - some_object may be
non-null, _before_  SomeObject ctor is finished. This is because JIT can
reorder instructions to write object address before executing constructor's
body (or maybe I'm wrong here?).

However, runtime has some means to deal with delayed initialization.
Note that we're talking only about _static_ properties here (perhaps,
volatile prefix will help with instance fields?)
If beforefieldinit flag is omited from type signature, then cctor will be
called only at the first access to type's static field or at invoking static
method of the class. This is similar to Java semantics for static field
initialization. And cctor is called only once.

However, I have no idea how to force C# compiler to generate such signatures
or prefixes.
I'm attaching IL source code to demonstrate how beforefieldinit influences
when the type constructor is invoked (try to uncomment beforefield init to
see the difference).



----- Original Message -----
From: "Alexander Klyubin" <klyubin@aqris.com>
To: <mono-list@ximian.com>
Sent: Sunday, October 28, 2001 12:20 PM
Subject: Re: [Mono-list] Class library developers: locking issues to keep in
mind


> I don't know that much about .NET memory model as of Java's one, but in
> Java the code with on-demand locking you provided is called
> Double-checked Locking (DCL). The problem with it is that it is not
> guaranteed to work in all JVMs as synchronizing memory thread's own
> local memory with shared main memory accessible to all threads can work
> differently in different JVMs. Maybe memory model is organized
> differently in .NET languages compared to Java. But as similarity
> between C# and Java is realy big, I suspect memory model should be quite
> similar if not the same.
>
> Here are good articles on this topic for Java. You migh also want to
> find out more by simply searching JavaWorld (www.javaworld.com) for
> "DCL". But these two are the core ones to understand the problem:
>
> 1. Double-checked locking: Clever, but broken
> http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html
>
> 2. Can double-checked locking be fixed?
> http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html
>
>
> Regards,
> Alexander Klyubin
>
> Miguel de Icaza wrote:
>
> >    I noticed today that I have been writing thread-unsafe code in my
> > code that goes into the class libraries.  Some classes for example
> > provide static properties, and to avoid a costly initialization at
> > bootstrap time, you want to create some of those values on demand
> > instead of doing it constructor, like this:
> >
> >    class Sample {
> > static SomeObject MyProperty {
> > get {
> > if (some_object == null)
> > some_object = new SomeObject ();
> > return some_object;
> > }
> > }
> >    }
> >
> >    Well, it turns out that the code above is not thread safe, because
> > two threads might be hitting the same spot at the same time and two
> > copies of SomeObject would be created.  Then later on, comparissions
> > would not work (if p == MyProperty).  To correct that situation, you
> > have to lock on either the instance (this) or the class (typeof (this)),
> > like this:
> >
> >     class Sample {
> > static SomeObject MyProperty {
> > get {
> > if (some_object != null)
> > return some_object;
> > lock (typeof (Sample)){
> > if (some_object == null)
> > some_object = new SomeObject ();
> > return some_object;
> > }
> > }
> > }
> >     }
> >
> >    Notice that you first test for initialization: if it is not null, you
> > can return the value without ever locking.  You only lock (which is an
> > expensive operation) if the value needs to be computed.
> >
> >    Now, notice how we test for the value *inside* the lock.  This is
> > important because the value might have been initialized in a separate
> > thread before we reach lock.
> >
> > Miguel.
> >
> >
> >
> >
> > _______________________________________________
> > Mono-list maillist  -  Mono-list@ximian.com
> > http://lists.ximian.com/mailman/listinfo/mono-list
> >
>
>
>
>
> _______________________________________________
> Mono-list maillist  -  Mono-list@ximian.com
> http://lists.ximian.com/mailman/listinfo/mono-list
>





------=_NextPart_000_0050_01C15FB2.D21B33A0
Content-Type: application/octet-stream;
	name="Memtest.il"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="Memtest.il"

.assembly memtest {}

.class public auto ansi sealed /*beforefieldinit*/ Singleton {

	.field public static initonly object Instance
	.field public static initonly int32 Num


	.method private hidebysig specialname rtspecialname static
       	void .cctor() cil managed /*synchronized*/
	{
		newobj instance void [mscorlib]System.Object::.ctor()
		stsfld object Singleton::Instance

		ldc.i4 0xACDC
		stsfld int32 Singleton::Num

		ldstr "Static init!"
		call void [mscorlib]System.Console::WriteLine(string)
		ret
	}


}



.class public auto ansi sealed beforefieldinit Test {

	.method static public void Main() il managed {
		.entrypoint

		ldstr "Started..."
		call void [mscorlib]System.Console::WriteLine(string)

		ldsfld object Singleton::Instance
		pop

		ldsfld object Singleton::Instance
		pop

		ret
	}

}

------=_NextPart_000_0050_01C15FB2.D21B33A0--