diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 7fc5c665918..f5d67625a8e 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -9,6 +9,8 @@ #include #include +using namespace std::chrono; + namespace Tasking { // That's cut down qtcassert.{c,h} to avoid the dependency. @@ -1848,14 +1850,95 @@ void TaskTreeTaskAdapter::start() task()->start(); } +using TimeoutCallback = std::function; + +struct TimerData +{ + system_clock::time_point m_deadline; + QPointer m_context; + TimeoutCallback m_callback; +}; + +QMutex s_mutex; +std::atomic_int s_timerId = 0; +QHash s_timerIdToTimerData = {}; +QMultiMap s_deadlineToTimerId = {}; + +static QList prepareForActivation(int timerId) +{ + QMutexLocker lock(&s_mutex); + const auto it = s_timerIdToTimerData.constFind(timerId); + if (it == s_timerIdToTimerData.cend()) + return {}; // the timer was already activated + + const system_clock::time_point deadline = it->m_deadline; + QList toActivate; + auto itMap = s_deadlineToTimerId.cbegin(); + while (itMap != s_deadlineToTimerId.cend()) { + if (itMap.key() > deadline) + break; + + const auto it = s_timerIdToTimerData.constFind(itMap.value()); + if (it != s_timerIdToTimerData.cend()) { + toActivate.append(it.value()); + s_timerIdToTimerData.erase(it); + } + itMap = s_deadlineToTimerId.erase(itMap); + } + return toActivate; +} + +static void removeTimerId(int timerId) +{ + QMutexLocker lock(&s_mutex); + const auto it = s_timerIdToTimerData.constFind(timerId); + QTC_ASSERT(it != s_timerIdToTimerData.cend(), + qWarning("Removing active timerId failed."); return); + + const system_clock::time_point deadline = it->m_deadline; + s_timerIdToTimerData.erase(it); + + const int removedCount = s_deadlineToTimerId.remove(deadline, timerId); + QTC_ASSERT(removedCount == 1, qWarning("Removing active timerId failed."); return); +} + +static void handleTimeout(int timerId) +{ + const QList toActivate = prepareForActivation(timerId); + for (const TimerData &timerData : toActivate) { + if (timerData.m_context) + QMetaObject::invokeMethod(timerData.m_context.get(), timerData.m_callback); + } +} + +static int scheduleTimeout(milliseconds timeout, QObject *context, const TimeoutCallback &callback) +{ + const int timerId = s_timerId.fetch_add(1) + 1; + const system_clock::time_point deadline = system_clock::now() + timeout; + QTimer::singleShot(timeout, context, [timerId] { handleTimeout(timerId); }); + QMutexLocker lock(&s_mutex); + s_timerIdToTimerData.emplace(timerId, TimerData{deadline, context, callback}); + s_deadlineToTimerId.insert(deadline, timerId); + return timerId; +} + TimeoutTaskAdapter::TimeoutTaskAdapter() { *task() = std::chrono::milliseconds::zero(); } +TimeoutTaskAdapter::~TimeoutTaskAdapter() +{ + if (m_timerId) + removeTimerId(*m_timerId); +} + void TimeoutTaskAdapter::start() { - QTimer::singleShot(*task(), this, [this] { emit done(true); }); + if (*task() == milliseconds::zero()) + QTimer::singleShot(0, this, [this] { emit done(true); }); + else + m_timerId = scheduleTimeout(*task(), this, [this] { m_timerId = {}; emit done(true); }); } } // namespace Tasking diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 78593aa1823..6962ed5cabe 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -430,7 +430,11 @@ class TASKING_EXPORT TimeoutTaskAdapter : public TaskAdapter m_timerId; }; } // namespace Tasking diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index b498720aa3d..cbe0048eeb4 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -781,7 +781,7 @@ void tst_Tasking::testTree_data() parallel, workflowPolicy(policy), createFailingTask(1, 1ms), - createSuccessTask(2, 2ms), + createSuccessTask(2, 1ms), groupDone(0), groupError(0) }; @@ -853,7 +853,7 @@ void tst_Tasking::testTree_data() workflowPolicy(policy), createSuccessTask(1), createFailingTask(2, 1ms), - createSuccessTask(3, 2ms), + createSuccessTask(3, 1ms), groupDone(0), groupError(0) };