How to store self-removing futures in a list - c++

I have some tasks that need to be performed asynchronously, and the server can't close while there are still tasks running. So I'm trying to store the futures returned by std::async in a list, but I also don't want to get an infinitely growing list of those. So I want to remove the futures as they're completed.
Here's roughly what I'm trying to do:
// this is a member of the server class
std::list<std::future<void>> pending;
std::list<std::future<void>>::iterator iter = ???;
pending.push_back( std::async( std::launch::async, [iter]()
{
doSomething();
pending.remove( iter );
} );
Here, iter needs to be pointing to the newly inserted element, but I can't get it before inserting the element (there is no iterator), nor after (since it is passed to the lambda by value). I could make a shared_ptr to store the iterator, but that seems to be way overkill.
Is there a better pattern for this?
Update: there seems to be another issue with this. When a future attempts to remove itself from the list, it is essentially waiting for itself to complete, which locks everything up. Oops!
On top of that, list destructor empties the list before calling element destructors.

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

Related

Multi-consumer condition variable wait in the same instance's member function

I'm having trouble thinking of a way to properly implement a signalling mechanism for multiple listeners waiting in the same function for a producer to signal some new data continuously, without getting "signalled" for the same previous data-
I want all listeners to always see the latest available data (not caring about missed signals if they are busy), without repeats.
My attempt so far:
#include <functional>
#include <shared_mutex>
#include <condition_variable>
#include <thread>
class Signaller {
public:
// Used by producer, will hold on to the mutex uniquely as it modifies data
void Signal(const std::function<void()>& fnIn) {
std::unique_lock lock(m_mtx);
fnIn();
m_newData = true;
m_cv.notify_all();
}
// Used by consumers, will only hold shared mutex to read data
void Wait(const std::function<void()>& fnIn) {
{
std::shared_lock lock(m_mtx);
m_cv.wait(lock, [this](){ return m_newData; });
fnIn();
}
// Need some way to flip m_newData to false when all threads are "done"
// (or some other method of preventing spurious wakeups)
// I don't think this is particularly ideal
{
std::unique_lock lock(m_mtx);
m_newData = false;
}
}
private:
std::condition_variable_any m_cv;
std::shared_mutex m_mtx;
bool m_newData{false}; // To prevent spurious wakeups
};
class Example {
public:
// Multiple threads will call this function in the same instance of Example
void ConsumerLoop()
{
int latestData{0};
while (true){
m_signaller.Wait([this, &latestData](){ latestData = m_latestData; });
// process latestData...
// I want to make sure latestData here is always the latest
// (It's OK to miss a few signals in between if its off processing this latest data)
}
}
// One thread will be using this to signal new data
void ProducerLoop(){
while(true){
int newData = rand();
m_signaller.Signal([this, newData](){ m_latestData = newData; });
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
private:
Signaller m_signaller;
int m_latestData;
};
My main issue (I think) is how to prevent spurious wakeups, while preventing repeated data from waking up the same thread. I've thought about using some sort of counter within each thread to keep track of whether it's receiving the same data, but couldn't get anywhere with that idea (unless I perhaps make some sort of map using std::this_thread::get_id?). Is there a better way to do this?
EDIT:
Expanding on my map of thread ID's idea, I think I've found a solution:
#include <functional>
#include <shared_mutex>
#include <condition_variable>
#include <unordered_map>
#include <thread>
class Signaller {
public:
// Used by producer, will hold on to the mutex uniquely as it modifies data
void Signal(const std::function<void()>& fnIn) {
std::unique_lock lock(m_mtx);
fnIn();
m_ctr++;
m_cv.notify_all();
}
void RegisterWaiter(){
std::unique_lock lock(m_mtx);
auto [itr, emplaced] = m_threadCtrMap.try_emplace(std::this_thread::get_id(), m_ctr);
if (!emplaced) {
itr->second = m_ctr;
}
}
// Used by consumers, will only hold shared mutex to read data
void Wait(const std::function<void()>& fnIn) {
std::shared_lock lock(m_mtx);
m_cv.wait(lock, [this](){ return m_threadCtrMap[std::this_thread::get_id()] != m_ctr; });
fnIn();
m_threadCtrMap[std::this_thread::get_id()] = m_ctr;
}
private:
std::condition_variable_any m_cv;
std::shared_mutex m_mtx;
std::uint32_t m_ctr{0};
std::unordered_map<std::thread::id, std::uint32_t> m_threadCtrMap; // Stores the last signalled ctr for that thread
};
class Example {
public:
// Multiple threads will call this function in the same instance of Example
void ConsumerLoop()
{
int latestData{0};
m_signaller.RegisterWaiter();
while (true){
m_signaller.Wait([this, &latestData](){ latestData = m_latestData; });
}
}
// One thread will be using this to signal new data
void ProducerLoop(){
while(true){
int newData = rand();
m_signaller.Signal([this, newData](){ m_latestData = newData; });
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
private:
Signaller m_signaller;
int m_latestData;
};
Here's my implementation:
#include <unordered_map>
#include <condition_variable>
#include <shared_mutex>
#include <thread>
/*
Example usage:
struct MyClass {
MultiCVSignaller m_signaller;
int m_latestData;
std::atomic<bool> m_stop{false};
~MyClass(){
m_stop = true;
m_signaller.Shutdown();
}
void FuncToWaitOnData() { // e.g. Multiple threads call this fn to "subscribe" to the signal
auto& signalCtr = m_signaller.RegisterListener();
while(!m_stop.load(std::memory_order_relaxed)) {
int latestDataInLocalThread;
// WaitForSignal() calls the provided function while holding on to the shared mutex
m_signaller.WaitForSignal(signalCtr, [this, &latestDataInLocalThread](){
latestDataInLocalThread = m_latestData;
});
// Make use of latest data...
}
}
void ProducerLoop() {
while(!m_stop.load(std::memory_order_relaxed)) {
// Signal() holds on to the mutex uniquely while calling the provided function.
m_signaller.Signal([&latestData](){
m_latestData = rand();
});
}
}
};
*/
class MultiCVSignaller
{
public:
using SignalCtr = std::uint32_t;
public:
MultiCVSignaller() = default;
~MultiCVSignaller() { Shutdown(); }
/*
Call to set and signal shutdown state, cancelling waits (and skipping the functions provided if any)
This should be added in the class' destructor before threads are joined.
*/
void Shutdown() {
std::unique_lock lock(m_mtx);
m_shutdown = true;
m_cv.notify_all();
}
// Calls the function if specified while holding on to the mutex with a UNIQUE lock
template<class Func = void(*)()>
void Signal(Func fnIn = +[]{})
{
std::unique_lock lock(m_mtx);
fnIn();
m_ctr++;
m_cv.notify_all();
}
MultiCVSignaller::SignalCtr& RegisterListener(){
std::unique_lock lock(m_mtx);
auto [itr, emplaced] = m_threadCtrMap.try_emplace(std::this_thread::get_id(), m_ctr);
if (!emplaced) {
itr->second = m_ctr;
}
return itr->second;
}
/*
Calls the optional function while holding on to the SHARED lock when signalled. The signalCtr argument should be provided by the return of RegisterListener() (see example)
*/
template<class Func = void(*)()>
void WaitForSignal(MultiCVSignaller::SignalCtr& signalCtr, Func fnIn = +[]{})
{
std::shared_lock lock(m_mtx);
m_cv.wait(lock, [this, &signalCtr](){ return ( m_shutdown || signalCtr != m_ctr); });
if (!m_shutdown)
{
fnIn();
signalCtr = m_ctr;
}
}
private:
std::condition_variable_any m_cv;
std::shared_mutex m_mtx;
bool m_shutdown{false};
SignalCtr m_ctr{0}; // Latest ctr from Signal()
// This map stores the signal count received for registered listeners.
// We use an unordered_map as references are never invalidated (unless erased),
// which is not the case for a vector
std::unordered_map<std::thread::id, SignalCtr> m_threadCtrMap;
};

(C++) How to use Payload Object to imiplement thread pool?

I saw this very well implemented thread pool: https://github.com/progschj/ThreadPool. I am wondering whether I can use a payload object instead. The idea is that instead of using a function pointer, use an object to describe the payload, which always contains a run function and a promise. The main thread then wait on the future of the promise.
Here is what I got:
#include <iostream>
#include <queue>
#include <thread>
#include <future>
#include <condition_variable>
#include <mutex>
class GenericPayload {
protected:
std::promise <int> m_returnCode;
public:
virtual void run() = 0;
std::future <int> getFuture() {
return m_returnCode.get_future();
}
};
class MyPayload:public GenericPayload {
private:
int m_input1;
int m_input2;
int m_result;
public:
MyPayload(int input1, int input2):m_input1(input1), m_input2(input2) {}
void run() {
m_result = m_input1 * m_input2;
m_returnCode.set_value(0);
}
int getResult() {
return m_result;
}
};
class ThreadPool {
private:
std::queue <GenericPayload *> payloads;
std::mutex queue_mutex;
std::condition_variable cv;
std::vector< std::thread > workers;
bool stop;
public:
ThreadPool(size_t threads)
: stop(false)
{
for(size_t i = 0;i<threads;++i)
workers.emplace_back(
[this]
{
for(;;)
{
GenericPayload *payload;
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->cv.wait(lock,
[this]{ return this->stop || !this->payloads.empty(); });
if(this->stop)
return;
payload = this->payloads.front();
this->payloads.pop();
}
payload->run();
}
}
);
}
void addPayLoad (GenericPayload *payload) {
payloads.push(payload);
}
~ThreadPool()
{
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
cv.notify_all();
for(std::thread &worker: workers)
worker.join();
}
};
int main() {
MyPayload myPayload(3, 5);
ThreadPool threadPool(2);
std::future <int> returnCode = myPayload.getFuture();
threadPool.addPayLoad(&myPayload);
returnCode.get();
std::cout << myPayload.getResult() << std::endl;
}
Is this the right way to do it though? I had to pass a pointer to the payload because 1. I want GenericPayload to be abstract and 2. std::promise is not copyable. Thx for any feedback.

Using smart pointer for critical section

What is the drawbacks or errors in the following approach to use non-owning std::unique_ptr having custom deleter to arrange a critical section?
#include <memory>
#include <shared_mutex>
#include <optional>
#include <variant>
#include <cassert>
struct Data
{
std::optional<int> i;
};
struct DataLocker
{
std::variant<std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex>> lock;
void operator () (const Data *)
{
std::visit([] (auto & lock) { if (lock) lock.unlock(); }, lock);
}
};
struct DataHolder
{
std::unique_ptr<Data, DataLocker> getLockedData()
{
return {&data, {std::unique_lock<std::shared_mutex>{m}}};
}
std::unique_ptr<const Data, DataLocker> getLockedData() const
{
return {&data, {std::shared_lock<std::shared_mutex>{m}}};
}
private :
mutable std::shared_mutex m;
Data data;
};
#include <iostream>
#include <thread>
int main()
{
DataHolder d;
auto producer = [&d]
{
d.getLockedData()->i = 123;
};
auto consumer = [&d = std::as_const(d)]
{
for (;;) {
if (const auto i = d.getLockedData()->i) {
std::cout << *i << std::endl;
return;
}
}
};
std::thread p(producer);
std::thread c(consumer);
p.join();
c.join();
}
One corner case, when writer reset()s a pointer and never destruct std::unique_ptr itself is covered by adding unlock to deleter's operator ().

Condition variable should be used or not to reduce missed wakeups

I have two threads, one is the producer and other is consumer. My consumer is always late (due to some costly function call, simulated in below code using sleeps) so I have used ring buffer as I can afford to loose some events.
Questions:
I am wondering if it would be better to use condition variable instead of what I currently have : continuous monitoring of the ring buffer size to see if the events got generated. I know that the current while loop of checking the ring buffer size is expensive, so I can probably add some yield calls to reduce the tight loop. I want to reduce the chances of dropped events.
Can I get rid of pointers? In my current code I am passing pointers to my ring buffer from main function to the threads. Wondering if there is any fancy or better way to do the same?
#include <iostream>
#include <thread>
#include <chrono>
#include <vector>
#include <atomic>
#include <boost/circular_buffer.hpp>
#include <condition_variable>
#include <functional>
std::atomic<bool> mRunning;
std::mutex m_mutex;
std::condition_variable m_condVar;
long int data = 0;
class Detacher {
public:
template<typename Function, typename ... Args>
void createTask(Function &&func, Args&& ... args) {
m_threads.emplace_back(std::forward<Function>(func), std::forward<Args>(args)...);
}
Detacher() = default;
Detacher(const Detacher&) = delete;
Detacher & operator=(const Detacher&) = delete;
Detacher(Detacher&&) = default;
Detacher& operator=(Detacher&&) = default;
~Detacher() {
for (auto& thread : m_threads) {
thread.join();
}
}
private:
std::vector<std::thread> m_threads;
};
void foo_1(boost::circular_buffer<int> *cb)
{
while (mRunning) {
std::unique_lock<std::mutex> mlock(m_mutex);
if (!cb->size())
continue;
int data = cb[0][0];
cb->pop_front();
mlock.unlock();
if (!mRunning) {
break;
}
//simulate time consuming function call
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
}
void foo_2(boost::circular_buffer<int> *cb)
{
while (mRunning) {
std::unique_lock<std::mutex> mlock(m_mutex);
cb->push_back(data);
data++;
mlock.unlock();
//simulate time consuming function call
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
int main()
{
mRunning = true;
boost::circular_buffer<int> cb(100);
Detacher thread_1;
thread_1.createTask(foo_1, &cb);
Detacher thread_2;
thread_2.createTask(foo_2, &cb);
std::this_thread::sleep_for(std::chrono::milliseconds(20000));
mRunning = false;
}
The producer is faster (16x) than the consumer, so ~93% of all events will always be discarded.

Weak binding using c++17

I am working on a processing framework where callbacks are registered to events and to ensure that no callback is invoked on an object, which has been deleted, I would like to use weak capture rather than capture by reference. It was no problem to make this work using C++14 and shared_from_this(), but how is this correctly achieved using C++17 and weak_from_this().
The example below prints nothing when C++17 is used. I am using g++ 6.3.0-18
#define CXX17 // When this is defined, nothing is printed
#ifdef CXX17
# include <experimental/memory>
# include <experimental/functional>
template <typename T>
using enable_shared_from_this = std::experimental::enable_shared_from_this<T>;
#else
# include <memory>
# include <functional>
template <typename T>
using enable_shared_from_this = std::enable_shared_from_this<T>;
#endif
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <iostream>
struct A : enable_shared_from_this<A> {
int a;
A() : a(7) {}
auto getptr() {
#ifdef CXX17
return this->weak_from_this();
#else
auto sptr = shared_from_this();
auto wptr = std::weak_ptr<decltype(sptr)::element_type>(sptr);
sptr.reset(); // Drop strong referencing
return wptr;
#endif
}
};
std::condition_variable condition;
std::mutex mutex;
std::atomic<bool> start0{false};
std::atomic<bool> start1{false};
std::shared_ptr<A> g_a;
static void thread_func0() {
auto w_a = g_a->getptr();
std::unique_lock<std::mutex> lock {mutex};
condition.wait(lock, [&]() {
return start0.load();
});
std::this_thread::sleep_for(std::chrono::microseconds(10));
if (auto t = w_a.lock()) {
std::cout << t->a << std::endl;
}
}
static void thread_func1() {
std::unique_lock<std::mutex> lock {mutex};
condition.wait(lock, [&]() {
return start1.load();
});
std::this_thread::sleep_for(std::chrono::microseconds(10000));
g_a = nullptr;
}
int main() {
g_a = std::make_shared<A>();
std::thread thread0(thread_func0);
std::thread thread1(thread_func1);
start0 = true;
start1 = true;
condition.notify_all();
thread0.join();
thread1.join();
return 0;
}
Here's a way more reduced example:
#include <experimental/memory>
#include <iostream>
template <typename T>
using enable_shared_from_this = std::experimental::enable_shared_from_this<T>;
struct A : enable_shared_from_this<A> {
int a;
A() : a(7) {}
};
int main() {
auto sp = std::make_shared<A>();
auto wp = sp->weak_from_this();
if (auto s = wp.lock()) {
std::cout << s->a << std::endl;
}
}
This prints nothing. Why? The reason is ultimately the reason why it's std::enable_shared_from_this and not some other type that you yourself can provide: the shared_ptr class needs to opt-in to this functionality. The new functionality is experimental, so std::shared_ptr was not opting in - so the underlying weak_ptr was never initialized. It just doesn't happen, so wp is always an "empty" weak_ptr here.
On the other hand, std::experimental::shared_ptr does opt-in to this functionality. You need to use the shared_ptr corresponding to your enable_shared_from_this - which is std::experimental::shared_ptr.
There's no std::experimental::make_shared (or at least, as far as I could find), but the opt-in mechanism isn't based on that anyway - it's just based on any shared_ptr construction. So if you change:
auto sp = std::make_shared<A>();
to:
auto sp = std::experimental::shared_ptr<A>(new A);
Then the opt-in mechanism matches the shared_ptr type and does the right thing, you get a valid weak_ptr (a std::experimental::weak_ptr), lock() gives you shared ownership of the underlying A, and the program prints 7.