Henk, Here are some problems: 1. Execution of the code in the same thread doesn't necessary means the execution on the same processor. 2. Dotnet memory model guarantees that reads and writes from the same processor to the *same memory location* would never cross each other, but no guarantees about read/write to the different memory locations. 3. Even so bNeedInit assignment is atomic, but there is no guarantee that that it will be visible to other processors in the same order as preceding assignments during <init stuff>.
... lock(this) { <init stuff> bNeedInit = false; //<--your code is vulnerable right here // other processors could already see bNeedInit value is false, // but previous assignments during <init stuff> could still be // not available to the other processors. // memory barrier will be placed during exit from lock block // on the next line. } // exits lock with all necessary memory barriers -Valery. P.S. of course your code is guaranteed to work on X86 architecture due to stronger memory model there. -----Original Message----- From: Henk de Koning [mailto:[EMAIL PROTECTED]] Sent: Monday, April 29, 2002 10:53 AM To: [EMAIL PROTECTED] Subject: Re: [DOTNET] lock - how expensive is it to call? Although I agree with you that there's more to synchronization in .Net than meets the eye (is this correct English btw ?), I don't think there's a problem with the code I posted. Here's why: 1) locks are internally implemented with volatile reads and writes (which makes tons of sense ;-) 2) according to the CLR memory model, reads and writes from the same processor will never cross each other 3) read/write reordening will never cross locks (they're implemented using volatile) 4) all instructions in a sync'ed block of code will run on one thread and hence on one processor QED -- Henkk BTW, the problem you mention is very real and exactly the reason I forked off the 'finding out if we need initialization' to a separate (valuetype) variable. BTW2, the C# volatile keyword is generally too rigid as it will cause *all* reads and writes to the variable to be volatile BTW3, bNeedInit is *not* a class with an implicit conversion operator for bool ;-) ----- Original Message ----- From: "Ian Griffiths" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Sent: Sunday, April 28, 2002 12:24 AM Subject: Re: [DOTNET] lock - how expensive is it to call? > "Henk de Koning" wrote: > > > > if (bNeedInit) { > > lock(this) { > > if (bNeedInit) { > > <init stuff> > > bNeedInit = false; > > } > > This is the classic double check lock for lazy initialization strategy. > Unfortunately, according to the strict definition of the .NET memory model, > this doesn't necessarily work - you can run into trouble due to the way that > the CLR allows multiprocessor caching architectures to reorder memory > accesses. > > (It's broken under Java too for more or less exactly the same reasons by the > way.) > > See Vance Morrison's excellent essay on this subject here: > > http://discuss.develop.com/archives/wa.exe?A2=ind0203B&L=DOTNET&P=R375 > > and the correction he posted after he realised it had a typo here: > > http://discuss.develop.com/archives/wa.exe?A2=ind0203B&L=DOTNET&P=R25512 > > > In brief, you will actually get away with the above code on the current CLRs > because Pentiums are relatively conservative about how they order their > memory accesses. But there is the potential for it to be broken. (Who > knows how it will behave on a multi-processor IA64 system, for example?) To > fix it, you need to use a memory barrier operation. Unfortunately the > memory barrier operations aren't directly available in the current release > of .NET - this article implies they will be in some future version. > (However, I believe you can get the same effect as the > System.Threading.Thread.MemoryBarrier(); by reading from and writing to a > volatile variable.) > > > -- > Ian Griffiths > DevelopMentor You can read messages from the DOTNET archive, unsubscribe from DOTNET, or subscribe to other DevelopMentor lists at http://discuss.develop.com.