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();
}