0

There are three question about the code snippet below.

  1. When the macro(i.e. NO_STUCK_WITH_OPTIMIZATION ) is not enabled, why this code snippet gets stuck when the optimization is enabled(i.e. -O1, -O2 or -O3) whereas the program works well if the optimization is not enabled?
  2. And why the program no longer get stuck if std::this_thread::sleep_for() is added?

UPDATED: 3. If the is_run is declared as volatile(for details, see the code snippet), then the program would never get stuck on X86? ^^UPDATED ENDS^^

    #include <functional>
    #include <thread>
    #include <iostream>
    #include <chrono>
    #include <atomic>
    
    #ifdef NO_STUCK_WITH_OPTIMIZATION 
    using TYPE = std::atomic<int>;
    #else
    using TYPE = int;  //The progrom gets stuck if the optimization is enabled.
    #endif
    
    int main()
    {
        TYPE is_run{1};
        auto thread = std::thread([&is_run](){while(1==is_run){
              //std::this_thread::sleep_for(std::chrono::milliseconds(10)); //If this line is added, the program is no longer gets stuck. Why?
                                            }
            std::cout << "thread game over" << std::endl;
        });
    
        std::this_thread::sleep_for(std::chrono::seconds(1));
        is_run = 0;
        thread.join();
    }
5
  • 2
    Just to make sure your question is understood, are you asking "how come when I use non thread-safe objects with multiple execution threads my code ends up being broken"? Commented Apr 3, 2022 at 13:05
  • See en.cppreference.com/w/cpp/language/… Commented Apr 3, 2022 at 13:05
  • Sorry for my poor English. My title hopes to indicate two questions. First, why the code snippet gets stuck when the optimization is enabled whereas the program works well if the optimization is not enabled. Second, why the code snippet does not get stuck any more if std::this_thread::sleep_for is added. Commented Apr 3, 2022 at 13:07
  • Because this is what "undefined behavior" means. Without proper thread safety, all guarantees are void. No refunds are given. Caveat emptor. You're on your own. The program may or may not work, and whether it does or does not work depends on compilation options, the time of day, the phase of the moon, current weather, or any other factor. It matters very little the reason for the specific manifestation of undefined behavior. It's undefined behavior. Commented Apr 3, 2022 at 13:10
  • Why the code snippet gets stuck when optimization is enabled? Because the program has a bug in it. It is using a variable in two threads, without interthread coordination such as an atomic or a mutex. Commented Apr 3, 2022 at 13:30

1 Answer 1

3

You have a multi-threaded program. One thread does is_run = 0;.

The other thread does while(1==is_run). Although you guarantee, with a sleep (matter for another question), that the write is done before the read, you need to tell the compiler to synchronize this variable.

In C++, the simple way to make sure one thread sees the change is to use atomic<int>. If you don't do this, the other thread might never see the change. This hiding of the change might be due to local optimization of the code at compile time, by the OS deciding not to refresh some memory page or by the hardware itself deciding the memory doesn't need to be reloaded.

Putting the atomic variable guarantees all those systems know what you want to do. So, use it. :-)

Lifting from the comments:

https://en.cppreference.com/w/cpp/language/memory_model#Threads_and_data_races

A program that has two conflicting evaluations has a data race unless both evaluations execute on the same thread or in the same signal handler, or both conflicting evaluations are atomic operations (see std::atomic), or one of the conflicting evaluations happens-before another (see std::memory_order) If a data race occurs, the behavior of the program is undefined.

Sign up to request clarification or add additional context in comments.

4 Comments

If the is_run is declared as volatile(for details, see the code snippet), then the program would never get stuck on X86?
@John Yes, with volatile the compiler is not allowed to optimize away the read so the program will work on x86. But I think the program is still technically undefined behavior., it might fail on platforms with looser memory models. Don't use volatile for multithreading purpose, only std::atomic or so.
yeah, don't use volatile. This keyword was added before C++ had a memory model. It will make some issues go away (by preventing some compiler optimization), but not all and your code would still have UB.
@Jeffrey I create a new question, let us discuss at here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.