Correctness of 'Concurrency in Action' atomic operation example












3















I've been studing 'Concurency in Action' position for some time and I have a problem with understanding following example of code (Listing 5.2):



#include <vector>
#include <atomic>
#include <iostream>

std::vector<int> data;
std::atomic<bool> data_ready(false);

void reader_thread()
{
while(!data_ready.load())
{
std::this_thread::sleep(std::milliseconds(1));
}
std::cout<<”The answer=”<<data[0]<<”n”;
}
void writer_thread()
{
data.push_back(42); //write of data
data_ready=true; //write to data_ready flag
}


The book explaines:




(...) The write of the data happens-before the write to the data_ready
flag (...)




My concern is that the sentence does not cover the out-of-order execution. From my understanding out of order execution may happen when at least two instruction do not have depended operands. Taking this into account:



data_ready=true



does not need anything from



data.push_back(42)


to be executed. As a result of that it is not guaranteed that:




The write of the data happens-before the write to the data_ready flag




Is my understadning correct or there is something in out-of-order execution that I don't understand causing misunderstaning of given example?



EDIT



Thank you for answers, it was helpful. My misunserstanding was a result of not knowing that atomic types not only prevents from partialy channing a variable, but also acts as memory barrier.



For example following code may be reordered in many combinations by either compiler or processor:



d=0;
b=5;
a=10
c=1;


Resulting with following order (one of many possibilities):



b=5;
a=10
c=1;
d=0;


It it is not a problem with single-thread code since none of expressions have depended operands on other, but on multithreaded application may result of undefined behaviour. For example following code (initial values: x=0 and y=0):



Thread 1:       Thread 2:   
x=10; while(y!=15);
y=15; assert(x==10);


Without reordering of code by compiler or reordering execution by processor we could say: "Since assigement y=15 allways happens after assigement x=10 and assert happens after while loop the assert will never fail" But it's not true. The real execution order may be as below (one of many possible combinations):



Thread 1:       Thread 2:   
x=10; (4) while(y!=15); (3)
y=15; (1) assert(x==10); (2)


By default an atomic variable ensures sequentionally consistent ordering. If y in example above was atomic with memory_order_seq_cst default parameter following sentences are true:
- what happens before in thread 1 (x=10) it is also visible in thread 2 as happening before.
- what happens after while(y!=15) in thread 2 it is also visible in thread 1 as happening after
As a result of it assert will never fail.



Some of sources that may help with understaning:




  • Memory model synchronization modes - GCC

  • CppCon 2015: Michael Wong “C++11/14/17 atomics and memory
    model..."

  • Memory barriers in C










share|improve this question

























  • Possible duplicate of what and how does happens-before happen in c++0x

    – Tsyvarev
    Dec 29 '18 at 13:35
















3















I've been studing 'Concurency in Action' position for some time and I have a problem with understanding following example of code (Listing 5.2):



#include <vector>
#include <atomic>
#include <iostream>

std::vector<int> data;
std::atomic<bool> data_ready(false);

void reader_thread()
{
while(!data_ready.load())
{
std::this_thread::sleep(std::milliseconds(1));
}
std::cout<<”The answer=”<<data[0]<<”n”;
}
void writer_thread()
{
data.push_back(42); //write of data
data_ready=true; //write to data_ready flag
}


The book explaines:




(...) The write of the data happens-before the write to the data_ready
flag (...)




My concern is that the sentence does not cover the out-of-order execution. From my understanding out of order execution may happen when at least two instruction do not have depended operands. Taking this into account:



data_ready=true



does not need anything from



data.push_back(42)


to be executed. As a result of that it is not guaranteed that:




The write of the data happens-before the write to the data_ready flag




Is my understadning correct or there is something in out-of-order execution that I don't understand causing misunderstaning of given example?



EDIT



Thank you for answers, it was helpful. My misunserstanding was a result of not knowing that atomic types not only prevents from partialy channing a variable, but also acts as memory barrier.



For example following code may be reordered in many combinations by either compiler or processor:



d=0;
b=5;
a=10
c=1;


Resulting with following order (one of many possibilities):



b=5;
a=10
c=1;
d=0;


It it is not a problem with single-thread code since none of expressions have depended operands on other, but on multithreaded application may result of undefined behaviour. For example following code (initial values: x=0 and y=0):



Thread 1:       Thread 2:   
x=10; while(y!=15);
y=15; assert(x==10);


Without reordering of code by compiler or reordering execution by processor we could say: "Since assigement y=15 allways happens after assigement x=10 and assert happens after while loop the assert will never fail" But it's not true. The real execution order may be as below (one of many possible combinations):



Thread 1:       Thread 2:   
x=10; (4) while(y!=15); (3)
y=15; (1) assert(x==10); (2)


By default an atomic variable ensures sequentionally consistent ordering. If y in example above was atomic with memory_order_seq_cst default parameter following sentences are true:
- what happens before in thread 1 (x=10) it is also visible in thread 2 as happening before.
- what happens after while(y!=15) in thread 2 it is also visible in thread 1 as happening after
As a result of it assert will never fail.



Some of sources that may help with understaning:




  • Memory model synchronization modes - GCC

  • CppCon 2015: Michael Wong “C++11/14/17 atomics and memory
    model..."

  • Memory barriers in C










share|improve this question

























  • Possible duplicate of what and how does happens-before happen in c++0x

    – Tsyvarev
    Dec 29 '18 at 13:35














3












3








3








I've been studing 'Concurency in Action' position for some time and I have a problem with understanding following example of code (Listing 5.2):



#include <vector>
#include <atomic>
#include <iostream>

std::vector<int> data;
std::atomic<bool> data_ready(false);

void reader_thread()
{
while(!data_ready.load())
{
std::this_thread::sleep(std::milliseconds(1));
}
std::cout<<”The answer=”<<data[0]<<”n”;
}
void writer_thread()
{
data.push_back(42); //write of data
data_ready=true; //write to data_ready flag
}


The book explaines:




(...) The write of the data happens-before the write to the data_ready
flag (...)




My concern is that the sentence does not cover the out-of-order execution. From my understanding out of order execution may happen when at least two instruction do not have depended operands. Taking this into account:



data_ready=true



does not need anything from



data.push_back(42)


to be executed. As a result of that it is not guaranteed that:




The write of the data happens-before the write to the data_ready flag




Is my understadning correct or there is something in out-of-order execution that I don't understand causing misunderstaning of given example?



EDIT



Thank you for answers, it was helpful. My misunserstanding was a result of not knowing that atomic types not only prevents from partialy channing a variable, but also acts as memory barrier.



For example following code may be reordered in many combinations by either compiler or processor:



d=0;
b=5;
a=10
c=1;


Resulting with following order (one of many possibilities):



b=5;
a=10
c=1;
d=0;


It it is not a problem with single-thread code since none of expressions have depended operands on other, but on multithreaded application may result of undefined behaviour. For example following code (initial values: x=0 and y=0):



Thread 1:       Thread 2:   
x=10; while(y!=15);
y=15; assert(x==10);


Without reordering of code by compiler or reordering execution by processor we could say: "Since assigement y=15 allways happens after assigement x=10 and assert happens after while loop the assert will never fail" But it's not true. The real execution order may be as below (one of many possible combinations):



Thread 1:       Thread 2:   
x=10; (4) while(y!=15); (3)
y=15; (1) assert(x==10); (2)


By default an atomic variable ensures sequentionally consistent ordering. If y in example above was atomic with memory_order_seq_cst default parameter following sentences are true:
- what happens before in thread 1 (x=10) it is also visible in thread 2 as happening before.
- what happens after while(y!=15) in thread 2 it is also visible in thread 1 as happening after
As a result of it assert will never fail.



Some of sources that may help with understaning:




  • Memory model synchronization modes - GCC

  • CppCon 2015: Michael Wong “C++11/14/17 atomics and memory
    model..."

  • Memory barriers in C










share|improve this question
















I've been studing 'Concurency in Action' position for some time and I have a problem with understanding following example of code (Listing 5.2):



#include <vector>
#include <atomic>
#include <iostream>

std::vector<int> data;
std::atomic<bool> data_ready(false);

void reader_thread()
{
while(!data_ready.load())
{
std::this_thread::sleep(std::milliseconds(1));
}
std::cout<<”The answer=”<<data[0]<<”n”;
}
void writer_thread()
{
data.push_back(42); //write of data
data_ready=true; //write to data_ready flag
}


The book explaines:




(...) The write of the data happens-before the write to the data_ready
flag (...)




My concern is that the sentence does not cover the out-of-order execution. From my understanding out of order execution may happen when at least two instruction do not have depended operands. Taking this into account:



data_ready=true



does not need anything from



data.push_back(42)


to be executed. As a result of that it is not guaranteed that:




The write of the data happens-before the write to the data_ready flag




Is my understadning correct or there is something in out-of-order execution that I don't understand causing misunderstaning of given example?



EDIT



Thank you for answers, it was helpful. My misunserstanding was a result of not knowing that atomic types not only prevents from partialy channing a variable, but also acts as memory barrier.



For example following code may be reordered in many combinations by either compiler or processor:



d=0;
b=5;
a=10
c=1;


Resulting with following order (one of many possibilities):



b=5;
a=10
c=1;
d=0;


It it is not a problem with single-thread code since none of expressions have depended operands on other, but on multithreaded application may result of undefined behaviour. For example following code (initial values: x=0 and y=0):



Thread 1:       Thread 2:   
x=10; while(y!=15);
y=15; assert(x==10);


Without reordering of code by compiler or reordering execution by processor we could say: "Since assigement y=15 allways happens after assigement x=10 and assert happens after while loop the assert will never fail" But it's not true. The real execution order may be as below (one of many possible combinations):



Thread 1:       Thread 2:   
x=10; (4) while(y!=15); (3)
y=15; (1) assert(x==10); (2)


By default an atomic variable ensures sequentionally consistent ordering. If y in example above was atomic with memory_order_seq_cst default parameter following sentences are true:
- what happens before in thread 1 (x=10) it is also visible in thread 2 as happening before.
- what happens after while(y!=15) in thread 2 it is also visible in thread 1 as happening after
As a result of it assert will never fail.



Some of sources that may help with understaning:




  • Memory model synchronization modes - GCC

  • CppCon 2015: Michael Wong “C++11/14/17 atomics and memory
    model..."

  • Memory barriers in C







c++ multithreading atomic






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 30 '18 at 11:17







Adam Stepniak

















asked Dec 29 '18 at 13:13









Adam StepniakAdam Stepniak

17419




17419













  • Possible duplicate of what and how does happens-before happen in c++0x

    – Tsyvarev
    Dec 29 '18 at 13:35



















  • Possible duplicate of what and how does happens-before happen in c++0x

    – Tsyvarev
    Dec 29 '18 at 13:35

















Possible duplicate of what and how does happens-before happen in c++0x

– Tsyvarev
Dec 29 '18 at 13:35





Possible duplicate of what and how does happens-before happen in c++0x

– Tsyvarev
Dec 29 '18 at 13:35












2 Answers
2






active

oldest

votes


















1














I understand your concerns, but the code from book is fine. Every operation with atomics is by default memory_order_seq_cst, which means that everything that happened before the write in one of threads happens before read in the rest. You can imagine atomic operations with this std::memory_order like this:



std::atomic<bool> a;
//equivalent of a = true
a.assign_and_make_changes_from_thread_visible(true);

//equvalent of a.load()
a.get_value_and_changes_from_threads();





share|improve this answer































    1














    From Effective Modern C++, Item 40, it says "std::atomics imposes restrictions on how code can be reordered, and one such restriction is that no code that, in the source cod, precedes a write of std::atomic variable may take place afterwards." The note is this is true for when using sequential consistency which is a fair assumption.






    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53969879%2fcorrectness-of-concurrency-in-action-atomic-operation-example%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      1














      I understand your concerns, but the code from book is fine. Every operation with atomics is by default memory_order_seq_cst, which means that everything that happened before the write in one of threads happens before read in the rest. You can imagine atomic operations with this std::memory_order like this:



      std::atomic<bool> a;
      //equivalent of a = true
      a.assign_and_make_changes_from_thread_visible(true);

      //equvalent of a.load()
      a.get_value_and_changes_from_threads();





      share|improve this answer




























        1














        I understand your concerns, but the code from book is fine. Every operation with atomics is by default memory_order_seq_cst, which means that everything that happened before the write in one of threads happens before read in the rest. You can imagine atomic operations with this std::memory_order like this:



        std::atomic<bool> a;
        //equivalent of a = true
        a.assign_and_make_changes_from_thread_visible(true);

        //equvalent of a.load()
        a.get_value_and_changes_from_threads();





        share|improve this answer


























          1












          1








          1







          I understand your concerns, but the code from book is fine. Every operation with atomics is by default memory_order_seq_cst, which means that everything that happened before the write in one of threads happens before read in the rest. You can imagine atomic operations with this std::memory_order like this:



          std::atomic<bool> a;
          //equivalent of a = true
          a.assign_and_make_changes_from_thread_visible(true);

          //equvalent of a.load()
          a.get_value_and_changes_from_threads();





          share|improve this answer













          I understand your concerns, but the code from book is fine. Every operation with atomics is by default memory_order_seq_cst, which means that everything that happened before the write in one of threads happens before read in the rest. You can imagine atomic operations with this std::memory_order like this:



          std::atomic<bool> a;
          //equivalent of a = true
          a.assign_and_make_changes_from_thread_visible(true);

          //equvalent of a.load()
          a.get_value_and_changes_from_threads();






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Dec 29 '18 at 13:20









          bartopbartop

          2,855826




          2,855826

























              1














              From Effective Modern C++, Item 40, it says "std::atomics imposes restrictions on how code can be reordered, and one such restriction is that no code that, in the source cod, precedes a write of std::atomic variable may take place afterwards." The note is this is true for when using sequential consistency which is a fair assumption.






              share|improve this answer




























                1














                From Effective Modern C++, Item 40, it says "std::atomics imposes restrictions on how code can be reordered, and one such restriction is that no code that, in the source cod, precedes a write of std::atomic variable may take place afterwards." The note is this is true for when using sequential consistency which is a fair assumption.






                share|improve this answer


























                  1












                  1








                  1







                  From Effective Modern C++, Item 40, it says "std::atomics imposes restrictions on how code can be reordered, and one such restriction is that no code that, in the source cod, precedes a write of std::atomic variable may take place afterwards." The note is this is true for when using sequential consistency which is a fair assumption.






                  share|improve this answer













                  From Effective Modern C++, Item 40, it says "std::atomics imposes restrictions on how code can be reordered, and one such restriction is that no code that, in the source cod, precedes a write of std::atomic variable may take place afterwards." The note is this is true for when using sequential consistency which is a fair assumption.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Dec 29 '18 at 13:22









                  Eddie C.Eddie C.

                  387




                  387






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53969879%2fcorrectness-of-concurrency-in-action-atomic-operation-example%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Monofisismo

                      compose and upload a new article using a custom form

                      How to correct the classpath of spring boot application so that it contains a single, compatible version of...