How to store self-removing futures in a list

It appears you can just append a default std::future to the list, get an iterator to that and then move your future in.

Mind you, that non-mutex-protected remove(iter) looks awfully dangerous.


Here's one way. I don't think this one needs futures:

#include <unordered_set>
#include <condition_variable>
#include <mutex>
#include <thread>

struct server
{
    std::mutex pending_mutex;
    std::condition_variable pending_condition;
    std::unordered_set<unsigned> pending;
    unsigned next_id =  0;

    void add_task()
    {
        auto lock = std::unique_lock(pending_mutex);
        auto id = next_id++;
        auto t = std::thread([this, id]{
            this->doSomething();
            this->notify_complete(id);
        });
        t.detach(); // or we could store it somewhere. e.g. pending could be a map
        pending.insert(id);
    }

    void doSomething();

    void notify_complete(unsigned id)
    {
        auto lock = std::unique_lock(pending_mutex);
        pending.erase(id);
        if (pending.empty())
            pending_condition.notify_all();
    }

    void wait_all_complete()
    {
        auto none_left = [&] { return pending.empty(); };

        auto lock = std::unique_lock(pending_mutex);
        pending_condition.wait(lock, none_left);
    }
};


int main()
{
    auto s = server();
    s.add_task();
    s.add_task();
    s.add_task();
    s.wait_all_complete();
}

Here it is with futures, in case that's important:

#include <unordered_map>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <future>

struct server
{
    std::mutex pending_mutex;
    std::condition_variable pending_condition;
    std::unordered_map<unsigned, std::future<void>> pending;
    unsigned next_id =  0;

    void add_task()
    {
        auto lock = std::unique_lock(pending_mutex);
        auto id = next_id++;
        auto f = std::async(std::launch::async, [this, id]{
            this->doSomething();
            this->notify_complete(id);
        });
        pending.emplace(id, std::move(f));
    }

    void doSomething();

    void notify_complete(unsigned id)
    {
        auto lock = std::unique_lock(pending_mutex);
        pending.erase(id);
        if (pending.empty())
            pending_condition.notify_all();
    }

    void wait_all_complete()
    {
        auto none_left = [&] { return pending.empty(); };

        auto lock = std::unique_lock(pending_mutex);
        pending_condition.wait(lock, none_left);
    }
};


int main()
{
    auto s = server();
    s.add_task();
    s.add_task();
    s.add_task();
    s.wait_all_complete();
}

Here's the list version:

#include <list>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <future>

struct server
{
    using pending_list = std::list<std::future<void>>;
    using id_type = pending_list::const_iterator;

    std::mutex pending_mutex;
    std::condition_variable pending_condition;
    pending_list pending;

    void add_task()
    {
        auto lock = std::unique_lock(pending_mutex);

        // redundant construction
        auto id = pending.emplace(pending.end());
        auto f = std::async(std::launch::async, [this, id]{
            this->doSomething();
            this->notify_complete(id);
        });
        *id = std::move(f);
    }

    void doSomething();

    void notify_complete(id_type id)
    {
        auto lock = std::unique_lock(pending_mutex);
        pending.erase(id);
        if (pending.empty())
            pending_condition.notify_all();
    }

    void wait_all_complete()
    {
        auto none_left = [&] { return pending.empty(); };

        auto lock = std::unique_lock(pending_mutex);
        pending_condition.wait(lock, none_left);
    }
};


int main()
{
    auto s = server();
    s.add_task();
    s.add_task();
    s.add_task();
    s.wait_all_complete();
}

Tags:

C++

C++17

Future