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.

Reply via email to