Here is a sample code illustrating my implementation (it is probably not working, as I'm replying from home, but I hope it is enough to get the idea):
class Task {
public:
Task(int tag) : tag_(tag) {};
void run() {
// do something
}
int getTag() { return tag_; }
private:
int tag_;
};
class Runner;
class WorkerPool {
public:
void scheduleRunner(Runner *runner);
};
class TaskQueue {
public:
void push(std::shared_ptr<Task> task);
std::shared_ptr<Task> pop();
bool isEmpty();
};
class Runner {
public:
Runner(WorkerPool& pool) : pool_(pool) {};
// schedule execution of the task.
// called by producers
void scheduleTask(std::shared_ptr<Task> task) {
std::unique_lock<std::mutex> lock(lock_);
queue_.push(std::move(task));
if (!running_) {
pool_.scheduleRunner(this);
running_ = true;
}
}
// run the task from the pool
void runTaskFromPool() {
std::unique_lock<std::mutex> lock(lock_);
std::shared_ptr<Task> task = queue_.pop();
// we can't leave it locked because the task may need
// to enqueue another element
lock.unlock();
task->run();
lock.lock();
if (queue_.isEmpty())
running_ = false;
else
pool_.scheduleRunner(this);
}
private:
WorkerPool& pool_;
TaskQueue queue_;
std::mutex lock_;
bool running_;
};
class Dispatcher {
public:
Dispatcher(WorkerPool& pool) : pool_(pool) {};
void scheduleTask(std::shared_ptr<Task> task) {
std::unique_lock<std::mutex> lock(lock_);
createRunnerIfNotExists_(task->getTag());
runners_[task->getTag()]->scheduleTask(std::move(task));
}
private:
std::map<int, std::unique_ptr<Runner>> runners_;
WorkerPool& pool_;
std::mutex lock_;
};