0

Im trying to make a EventTask that calls a function passed in a loop.

I need it to wait to start then mark it as finished.

My problem is i dont know how to receive the arguments from my wait function to pass to the function that is called

As you can see the problem is in my taskFunc _event.wait should set the arguments to pass onto the function.

class Event
{
public:

    Event() : _signalled(false) {}

    virtual inline void notify(){
        std::unique_lock<std::mutex> lock(_mutex);
        _signalled = true;
        _condvar.notify_one();
    }

    virtual inline void wait(){
        std::unique_lock<std::mutex> lock(_mutex);
        _condvar.wait(lock, [&] { return _signalled; });
        _signalled = false;

        stop();
    }

    virtual inline void stop(){
        std::unique_lock<std::mutex> lock(_mutex);
        _signalled = false;
    }

private:

    std::mutex _mutex;
    std::condition_variable _condvar;
    bool _signalled;
};

template <class T>
class EventArg : public Event
{
public:

    virtual inline void notify(T arg){
        Event::notify();
        this->arg = arg;
    }

    virtual inline void wait(T& arg){
        Event::wait();
        arg = this->arg;
    }

private:
    T arg;
};

template<class... Args>
class EventTask
{
public:
    EventTask(std::function<void(Args...)> func) : m_Function(func), m_Run(true), m_thread(&taskFunc, this) {}

    void notify(Args&& ...Params) { 
        _Event.notify(std::forward<Args>(Params)...); }

    void wait() { 
        _EventFinished.wait(); }

    void stop() {
        m_stop = true;
        _Event.stop();
    }

private:
    void taskFunc()
    {
        void* pArg = nullptr;
        while (m_Run){
            _Event.wait(pArg);
            m_Function(std::forward<Args>(Params)...);
            _EventFinished.notify();
        }
    }

private:
    std::function<void(Args...)> m_Function;
    bool m_Run;
    std::thread m_thread;
    EventArg<Args...> _Event;
    Event _EventFinished;
};
  • 1
    Can you please rephrase your question? Honestly i've no idea, what you're trying to achieve. Maybe instead of writing, how you're solving your problem, just write your problem? – Radosław Cybulski Jul 02 '19 at 08:42
  • 2
    Using `inline` with methods defined inside class is redundant - those are already inlined. With newest compilers using `inline` is redundant almost always anyway, as compilers are usually smart enough to figure out that anyway. `inline` doesn't work with virtual functions very well anyway. You're trying also to mix templates with virtual functions and those two concepts rarely go together well. – Radosław Cybulski Jul 02 '19 at 08:43
  • @RadosławCybulski i worked out what imkinda trying to do is make a threadpool that keeps the threads open, so its not always creating new threads. iwanted to be able to pass arugments to it and then read them –  Jul 02 '19 at 09:37
  • So you want to have threadpool, into which you insert function(s). Every function waits for it's arguments to be updated, process them and waits again in a loop? – Radosław Cybulski Jul 02 '19 at 10:50
  • @RadosławCybulski yeah –  Jul 02 '19 at 11:24

1 Answers1

0

Try this:

#include <iostream>
#include <functional>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <tuple>

template<class... Args>
class EventTask
{
public:
    EventTask(std::function<void(Args...)> func) : m_Function(func), m_Run(true) {
        m_thread = std::thread{ [=]() {
            taskFunc();
        }};
    }
    ~EventTask() {
        stop();
        m_thread.join();
    }

    void notify(const std::tuple<Args...> &args) {
        std::unique_lock<std::mutex> lock(_mutex);
        _signalled = true;
        _args = args;
        _condvar.notify_all();
    }

    void stop() {
        m_Run = false;
        _condvar.notify_all();
    }

private:
    void taskFunc()
    {
        std::tuple<Args...> args;
        while (true){
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _condvar.wait(lock, [&] { return m_Run && _signalled; });
                if (!m_Run) break;
                _signalled = false;
                args = _args;
            }
            std::apply(m_Function, args);
            //_EventFinished.notify();
        }
    }

private:
    std::function<void(Args...)> m_Function;

    std::tuple<Args...> _args;
    std::mutex _mutex;
    std::condition_variable _condvar;
    bool _signalled = false;

    //Event _EventFinished;
    bool m_Run;
    std::thread m_thread;
};

int main()
{
    EventTask<int, int> ma{ [](int a, int b) {

    }};
    ma.notify({ 1, 2 });
}

What is going on here? There are two threads, "producer" thread (the one, that produces arguments for function, hence the name) and "consumer" thread (the one, that actually does the running).

"producer" thread locks mutex, copies arguments and notifies, that there is something to be done. "consumer" thread locks mutex, then waits on condition. Waiting on condition (and mutex) releases the mutex, which will be reaquired, when notification on condition variable comes. When "producer" variable sets arguments, "consumer" will awoke, reacquire the mutex (this is required, otherwise "producer" might set args twice in a row resulting a race, which is undefined behavior), once again copies arguments and releases mutex. Then it continues with calling the worker function, using it's own local copy of arguments.

Similar process goes, when you try to stop the whole thing. "producer" locks mutex, sets m_Run to false and notifies everyone. "consumer" thread awoke, notifies, that m_Run is false and breads from the loop, ending it's thread. Note, that this won't break worker function, that is already in progress - you've to wait (note the call to join in destructor) for it to finish.

Radosław Cybulski
  • 2,952
  • 10
  • 21
  • That seems like what im looking for thankyou, i only have 1 problem 'std::apply' does not seem to work for me –  Jul 03 '19 at 04:14
  • You need c++17 for std::apply. There is plenty of replacements for c++11 code on stack overflow, for example this https://stackoverflow.com/questions/687490/how-do-i-expand-a-tuple-into-variadic-template-functions-arguments . – Radosław Cybulski Jul 03 '19 at 04:47