locked
CLR 2.0 Memory Model RRS feed

  • Question

  • This example is from the PLDI 2009 slides. I ported it to C# 3.0 ( VS 2008 ):

    public static boolRun()
    {
      bool flag = false;
    
      int data = 0;
      int read = 0;
    
      var t2 = new Thread( o =>
        {
          while ( !flag ) Thread.Sleep( 0 );
          read = data; // race
        } );
      t2.Start();
    
      data = 1; // race
      flag = true;
    
      t2.Join();
    
      return ( read == 1 );
    }
    
    Chess reports two races. The first is for the "flag" variable, which I understand.

    The second detected race is for the "data" variable on the lines shown in the code above. As I understand the CLR 2.0 Memory Model, this should not be a race. Since writes may not be reordered, if flag == true in thread t2, then the write to "data" must also be visible.

    Have I misunderstood something?

    Thanks,
    Nick
    Saturday, February 6, 2010 1:46 PM

Answers

  • Hi Nicholas,

    The important thing here is to keep definitions straight. In particular, the following are not synonymous:

    (a) code works correctly on CLR 2.0 memory model     vs.    (b)  code is data race free

    Property (b) does imply (a), but not vice versa. Our definition of data race is more conservative. So, in order to keep your programs data race free, you may have to add a few more volatile declarations than if you are writing directly against the CLR 2.0 memory model.

    In the example you show, the data race on "data" goes away as soon as you change the declaration of "bool flag = false;" to the correct declaration of "volatile bool flag = false;", which is something that should definitely be done.

    You may be concerned about performance impact when making variables volatile. However, note that this is VERY unlikely to do you harm (performance of volatile accesses is almost always exactly the same as normal accesses, at least on x86, where they get compiled to the same basic loads and stores), and is much better software engineering becuase your code declares its intent (use of flag variable for synchronization purposes rather than for storing data). 

    Our definition of data race is similar to the Java definition (two conflicting acesses that are not ordered by synchronization), and will guarantee that any data-race-free program works  correctly not just under CLR 2.0, but under the CLR ECMA model (e.g. on Itanium) or on potential future variations of the CLR memory model.

    I would like to point you to a good place for the precise definition of data race we are using but turns out the PLDI slides are really all I have right now. We should probably provide more info on that.

    Monday, February 8, 2010 6:09 PM
    Answerer

All replies

  • Hi Nicholas,

    The important thing here is to keep definitions straight. In particular, the following are not synonymous:

    (a) code works correctly on CLR 2.0 memory model     vs.    (b)  code is data race free

    Property (b) does imply (a), but not vice versa. Our definition of data race is more conservative. So, in order to keep your programs data race free, you may have to add a few more volatile declarations than if you are writing directly against the CLR 2.0 memory model.

    In the example you show, the data race on "data" goes away as soon as you change the declaration of "bool flag = false;" to the correct declaration of "volatile bool flag = false;", which is something that should definitely be done.

    You may be concerned about performance impact when making variables volatile. However, note that this is VERY unlikely to do you harm (performance of volatile accesses is almost always exactly the same as normal accesses, at least on x86, where they get compiled to the same basic loads and stores), and is much better software engineering becuase your code declares its intent (use of flag variable for synchronization purposes rather than for storing data). 

    Our definition of data race is similar to the Java definition (two conflicting acesses that are not ordered by synchronization), and will guarantee that any data-race-free program works  correctly not just under CLR 2.0, but under the CLR ECMA model (e.g. on Itanium) or on potential future variations of the CLR memory model.

    I would like to point you to a good place for the precise definition of data race we are using but turns out the PLDI slides are really all I have right now. We should probably provide more info on that.

    Monday, February 8, 2010 6:09 PM
    Answerer
  • Sebastian,

    Sorry for my delayed response.

    That makes perfect sense, thank you.

    Nick
    Thursday, February 18, 2010 12:57 PM
  • Hello.

    What is if

    int data=0; where replaced with List<Object> data=new List<Object>();

    data=1;//race replaced with data.Add(new object());// race

    and in t2 read=data replaced with read=data.Count;

     

    Would adding volatile to flag (and only to flag ) be still enougth to remove all races from the code?

    Does the synchronising provided by a volatile write on flag in t1 and a volatile read in t2 make sure that t2 reads after the volatile read will be accurate for all data written by t1 before volatile write, even is that data is any complex class, as long as t1 does not use it anymore (no concurrent access, just t2 accessing after t1 changed)?

    Saturday, April 17, 2010 11:44 AM
  •  

    Good question.

    Yes, no problem: it works if data is a complex object as well.

    (it would be a different story if flag were a complex object though: that would not work, you would need to use a thread-safe collection)

    Sebastian

     

    Tuesday, April 20, 2010 8:13 PM
    Answerer
  • Yes, no problem: it works if data is a complex object as well.

    (it would be a different story if flag were a complex object though: that would not work, you would need to use a thread-safe collection)

    Thanks. I'm getting the story I think...

    The whole thing is about the volatile write in t1, that makes sure that all previous-in-code changes from t1 are done in memory before it, and the volatile read from t2, that does the opposite for reading, thus making sure that t2 sees accurate data modified by t1 before the volatile write.

    I undersand your "if flag were a compex object" like that: if flag is a complex class marked as volatile:

    • If t1 changes SOME CONTENT (not volatile) of flag, and t2 accesses that content, then 2 problems:
      I have a race on the content of flag, which could be a problem depending of its type and use, and may require synchronization.
      No volatile write/read occurs after t1 sets data, and therefore it would not work anymore for data (should be a thread safe collection then for example).
    • If t1 initializes flag to not null after setting data, and t2 checks flag for non null (but does not accesses its contents), then everything if fine again, the volatile write/read occurs.

     

    Hope I understood you well,

    Sylvain.

    Wednesday, April 21, 2010 10:28 AM
  • Hi Nicholas,

    The important thing here is to keep definitions straight. In particular, the following are not synonymous:

    (a) code works correctly on CLR 2.0 memory model     vs.    (b)  code is data race free

    Property (b) does imply (a), but not vice versa. Our definition of data race is more conservative. So, in order to keep your programs data race free, you may have to add a few more volatile declarations than if you are writing directly against the CLR 2.0 memory model.

    In the example you show, the data race on "data" goes away as soon as you change the declaration of "bool flag = false;" to the correct declaration of "volatile bool flag = false;", which is something that should definitely be done.

    You may be concerned about performance impact when making variables volatile. However, note that this is VERY unlikely to do you harm (performance of volatile accesses is almost always exactly the same as normal accesses, at least on x86, where they get compiled to the same basic loads dvd and stores), and is much better software engineering becuase your code declares its intent (use of flag variable for synchronization purposes rather than for storing data). 

    Our definition of data race is similar to the Java definition (two conflicting acesses that are not ordered by synchronization), and will guarantee that any data-race-free program works  correctly not just under CLR 2.0, but under the CLR ECMA model (e.g. on Itanium) or on potential future variations of the CLR memory model.

    I would like to point you to a good place for the precise definition of data race we are using but turns out the PLDI slides are really all I have right now. We should probably provide more info on that.


    Thank you very much for your clear explanation. Now I understand more clearly about this.
    Friday, July 2, 2010 4:35 PM