size_t ExecutorPool::_schedule(ExTask task) {
LockHolder lh(tMutex);
+ const size_t taskId = task->getId();
+
TaskQueue* q = _getTaskQueue(task->getTaskable(),
GlobalTask::getTaskType(task->getTypeId()));
TaskQpair tqp(task, q);
- taskLocator[task->getId()] = tqp;
- q->schedule(task);
+ auto result = taskLocator.insert(std::make_pair(taskId, tqp));
+
+ if (result.second) {
+ // tqp was inserted; it was not already present. Prevents multiple
+ // copies of a task being present in the task queues.
+ q->schedule(task);
+ }
- return task->getId();
+ return taskId;
}
size_t ExecutorPool::schedule(ExTask task) {
size_t getNumReadyTasks(task_type_t qType) {
return numReadyTasks[qType];
}
+
+ std::map<size_t, TaskQpair> getTaskLocator() {
+ return taskLocator;
+ };
+
private:
void cancelAll_UNLOCKED() {
for (auto& it : taskLocator) {
EXPECT_EQ(2, runCount);
}
+
+/* Testing to ensure that repeatedly scheduling a task does not result in
+ * multiple entries in the taskQueue - this could cause a deadlock in
+ * _unregisterTaskable when the taskLocator is empty but duplicate tasks remain
+ * in the queue.
+ */
+TEST_F(SingleThreadedExecutorPoolTest, ignore_duplicate_schedule) {
+ ExTask task = new LambdaTask(
+ taskable, TaskId::ItemPager, 10, true, [&] { return false; });
+
+ size_t taskId = task->getId();
+
+ ASSERT_EQ(taskId, pool->schedule(task));
+ ASSERT_EQ(taskId, pool->schedule(task));
+
+ std::map<size_t, TaskQpair> taskLocator =
+ dynamic_cast<SingleThreadedExecutorPool*>(ExecutorPool::get())
+ ->getTaskLocator();
+
+ TaskQueue* queue = taskLocator.find(taskId)->second.second;
+
+ EXPECT_EQ(1, queue->getFutureQueueSize())
+ << "Task should only appear once in the taskQueue";
+
+ pool->cancel(taskId, true);
+}
#include <executorpool.h>
#include <executorthread.h>
+#include <fakes/fake_executorpool.h>
#include <gtest/gtest.h>
#include <taskable.h>
#include <thread>
class ExecutorPoolTest : public ::testing::Test {};
+class SingleThreadedExecutorPoolTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ SingleThreadedExecutorPool::replaceExecutorPoolWithFake();
+ pool = ExecutorPool::get();
+ pool->registerTaskable(taskable);
+ }
+
+ void TearDown() override {
+ pool->unregisterTaskable(taskable, false);
+ pool->shutdown();
+ }
+
+ ExecutorPool* pool;
+ MockTaskable taskable;
+};
+
class ExecutorPoolDynamicWorkerTest : public ExecutorPoolTest {
protected:
void SetUp() override {