7

I have a function that returns a std::future. I have added a cache to the implementation, and I would like to optionally return a value immediately if it does not need to be recalculated.

How can I create an already-resolved future?

// Class declarations shortened to minimal example to communicate intent
class MyClass {
    Cache m_cache;

    std::future<int> foo(int arg);
}

class Cache {
    std::optional<int> get(int arg);
}

std::future<int> MyClass::foo(int arg) {
    if (auto res = m_cache.get(arg)) {
        // *res is my result
        return std::future(*res); // ????? Doesn't work
    }
    // If the cache misses, we need to calculate it
    // Fire up the ol' thread pool and get to work
    return std::launch(std::launch::async /* ommited for brevity */);
}

I'm targeting C++20.

Aamir
  • 1,974
  • 1
  • 14
  • 18
Carson
  • 2,700
  • 11
  • 24
  • 1
    Unrelated to your actual question, Java's `concurrentHashMap` has locks on each *entry* in the "cache", and on a cache miss, it will insert a locked placeholder, and then fire async work to "calculate the value and unlock the entry". That way, if many threads ask for the same cache-miss item, only one calculates it, and the rest simply block, and thus each entry is only calculated exactly once. – Mooing Duck Jul 14 '23 at 19:47

1 Answers1

9

Only std::async, std::packaged_task and std::promise can create futures with new states.

std::promise is the easiest way:

std::future<int> MyClass::foo(int arg) {
    if (auto res = m_cache.get(arg)) {
        // *res is my result
        std::promise<int> p;
        p.set_value(*res);
        return p.get_future();
    }
    // If the cache misses, we need to calculate it
    // Fire up the ol' thread pool and get to work
    return std::launch(std::launch::async /* ommited for brevity */);
}
// Or with a helper function
template<typename T>
std::future<std::decay_t<T>> make_ready_future(T&& value) {
    std::promise<std::decay_t<T>> p;
    p.set_value(std::forward<T>(value));
    return p.get_future();
}

std::future<int> MyClass::foo(int arg) {
    if (auto res = m_cache.get(arg)) {
        // *res is my result
        return make_ready_future(*res);
    }
    // If the cache misses, we need to calculate it
    // Fire up the ol' thread pool and get to work
    return std::launch(std::launch::async /* ommited for brevity */);
}
Artyer
  • 31,034
  • 3
  • 47
  • 75