BTW: This kind of lazy init problem could be corrected following way: if (fNeedInit) { lock (this) { if (fNeedInitInt) { <init stuff> fNeedInitInt = false; } } //write barrier is performed by lock's release. fNeedInit = false; }
Note: second if checks internal fNeedInitInt variable and fNeedInit is updated outside of lock region. (of course we consider that bNeedInit/fNeedInit/fNeedInitInt are not volatile). -Valery. -----Original Message----- From: Valery Pryamikov Sent: Monday, April 29, 2002 11:55 AM To: 'dotnet discussion' Subject: RE: Re: [DOTNET] lock - how expensive is it to call? Henk, Here are some problems: 1. Execution of the code on the same thread doesn't necessaraly mean execution of the code 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 it will be visible to other processors in the same order as preceding assignments during <init stuff>. if (bNeedInit) { lock(this) { if (bNeedInit) { <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; > > } > You can read messages from the DOTNET archive, unsubscribe from DOTNET, or subscribe to other DevelopMentor lists at http://discuss.develop.com.