James
James

Reputation: 9278

Is it thread-safe to declare a std::future class member as being mutable?

Is it safe (thread-safe) to declare a std::future as being mutable since its get() function changes its state. I assume that it's like std::mutex which is safe to be made mutable.

template <typename T>
struct S {
    void query() {
        m_fut = doAsyncQuery();
    }

    template <typename L>
    void get(L lambda) const {
        lambda(m_f.get());
    }

    mutable std::future<T> m_f;
};

Upvotes: 4

Views: 1485

Answers (1)

Max Vollmer
Max Vollmer

Reputation: 8598

is it thread-safe?

No, but that doesn't matter.

std::future is intended for use in a single thread and is not thread-safe by design. It makes no sense to share it between multiple threads. You are supposed to call get() only once and then throw it away.

(The thread that runs the attached operation and sets the value returned by get() is not relevant here. The implementation manages and hides the necessary synchronization for you.)

(Of course you can transfer ownership of the std::future from one thread to another. But at any given time only one thread should hold and use the std::future.)

can it be mutable?

Yes. In fact, std::future cannot be const since its state changes by definition when the attached operation completes and when you call get().

However mutable should not be the keyword to look for here. Of course you can wrap a std::future in a class or struct with the mutable keyword and then create a const instance of that class, but you are hiding away an important piece of information (namely that your wrapper is not const at all). (It might make sense in a more complex class, but I would argue that a complex class with an std::future member is a sign of bad design.)

Instead you should make such a wrapper class or struct non-const as well:

template <typename T>
struct S {
    void query() {
        m_fut = doAsyncQuery();
    }

    template <typename L>
    void get(L lambda) {    // remove const here
        lambda(m_f.get());
    }

    std::future<T> m_f;     // remove mutable here
};

As long as you are aware that instances of S must not be const, that you can call get only once, and that you shouldn't share an S between threads, you should be safe.

Upvotes: 2

Related Questions