How can I cancel a std::async function?

danijar picture danijar · Jan 13, 2013 · Viewed 8.3k times · Source

Possible Duplicate:
Is there a way to cancel/detach a future in C++11?

There is a member function which runs asynchronously using std::future and std::async. In some case, I need to cancel it. (The function loads near objects consecutively and sometimes an objects gets out of range while loading it.) I already read the answers to this question addressing the same issue, but I cannot get it work.

This is simplified code with the same structure as my actual program has. Calling Start() and Kill() while the asynchronous is running, causes a crash because of access violation for input.

In my eyes the code should work as follows. When Kill() is called, the running flag is disabled. The next command get() should wait for thread to end, which it does soon since it checks the running flag. After the thread is canceled, the input pointer is deleted.

#include <vector>
#include <future>
using namespace std;

class Class
{
    future<void> task;
    bool running;
    int *input;
    vector<int> output;

    void Function()
    {
        for(int i = 0; i < *input; ++i)
        {
            if(!running) return;
            output.push_back(i);
        }
    }

    void Start()
    {
        input = new int(42534);
        running = true;
        task = async(launch::async, &Class::Function, this);
    }

    void Kill()
    {
        running = false;
        task.get();
        delete input;
    }
};

It seems like the thread doesn't notice toggling the running flag to false. What is my mistake?

Answer

Jonathan Wakely picture Jonathan Wakely · Jan 13, 2013

Since noone's actually answered the question yet I'll do so.

The writes and reads to the running variable are not atomic operations, so there is nothing in the code that causes any synchronisation between the two threads, so nothing ever ensures that the async thread sees that the variable has changed.

One possible way that can happen is that the compiler analyzes the code of Function, determines that there are never any writes to the variable in that thread, and as it's not an atomic object writes by other threads are not required to be visible, so it's entirely legal to rearrange the code to this:

void Function()
{
    if(!running) return;
    for(int i = 0; i < *input; ++i)
    {
        output.push_back(i);
    }
}

Obviously in this code if running changes after the function has started it won't cause the loop to stop.

There are two ways the C++ standard allows you to synchronize the two threads, which is either to use a mutex and only read or write the running variable while the mutex is locked, or to make the variable an atomic variable. In your case, changing running from bool to atomic<bool> will ensure that writes to the variable are synchronized with reads from it, and the async thread will terminate.