6

I want to create an event loop class that will run on it's own thread, support adding of tasks as std::functions and execute them. For this, I am using the SafeQueue from here: https://stackoverflow.com/a/16075550/1069662

class EventLoop
{
public:
typedef std::function<void()> Task;

EventLoop(){ stop=false; }

void add_task(Task t) { queue.enqueue(t); }

void start();

void stop() { stop = true; }

private:
    SafeQueue<Task> queue;
    bool stop;
};

void EventLoop::start()
{
    while (!stop) {
        Task t = queue.dequeue(); // Blocking call
        if (!stop) {
            t();
        }
    }    

    cout << "Exit Loop";
}

Then, you would use it like this:

EventLoop loop;
std::thread t(&EventLoop::start, &loop);

loop.add_task(myTask);
// do smth else

loop.stop();
t.join();

My question is: how to stop gracefully the thread ? Here stop cannot exit the loop because of the blocking queue call.

Community
  • 1
  • 1
yandreiy
  • 181
  • 2
  • 10
  • As a note, I think it's probably helpful to have an `idle/repeat` list as well. – Mooing Duck Oct 18 '14 at 17:45
  • Is there a `bool SafeQueue::try_dequeue(Task& out, std::chrono::milliseconds timeout);` or similar? – Mooing Duck Oct 18 '14 at 17:48
  • If possible I would also consider TBB's [concurrent queue](http://www.threadingbuildingblocks.org/docs/help/tbb_userguide/Concurrent_Queue_Classes.htm) containers for high performance rather than relying on a simple blocking mutex. – sjdowling Oct 18 '14 at 19:54
  • @sjdowling Thanks, TBB looks interesting – yandreiy Oct 18 '14 at 20:12

2 Answers2

5

Queue up a 'poison pill' stop task. That unblocks the queue wait and either directly requests the thread to clean up and exit or allows the consumer thread to check a 'stop' boolean.

That's assuming you need to stop the threads/task at all before the app terminates. I usually try to not do that, if I can get away with it.

Martin James
  • 24,453
  • 3
  • 36
  • 60
1

An alternative approach: just queue up a task that throws an exception. A few changes to your code:

class EventLoop {

// ...

    class stopexception {};

// ...


    void stop()
    {
          add_task(
               // Boring function that throws a stopexception
          );
    }
};

void EventLoop::start()
{
    try {
        while (1)
        {
            Task t = queue.dequeue(); // Blocking call
            t();
        }
    } catch (const stopexception &e)
    {
        cout << "Exit Loop";
    }
}

An alternative that doesn't use exceptions, for those who are allergic to them, would be to redefine Task as a function that takes an EventLoop reference as its sole parameter, and stop() queues up a task that sets the flag that breaks out of the main loop.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148