forked from qt-creator/qt-creator
TaskTree: Replace the usages of old WaitFor with new Barrier
Adapt the TaskTree tests and the usage in FileStreamer. The FileStreamer may be tested by running the FileSystemAccessTest. Change-Id: I1d8086dd359c458b7bdd3d4d47cf249184b04c65 Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include "filestreamer.h"
|
#include "filestreamer.h"
|
||||||
|
|
||||||
#include "asynctask.h"
|
#include "asynctask.h"
|
||||||
|
#include "barrier.h"
|
||||||
#include "qtcprocess.h"
|
#include "qtcprocess.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
@@ -321,7 +322,7 @@ static Group sameRemoteDeviceTransferTask(const FilePath &source, const FilePath
|
|||||||
static Group interDeviceTransferTask(const FilePath &source, const FilePath &destination)
|
static Group interDeviceTransferTask(const FilePath &source, const FilePath &destination)
|
||||||
{
|
{
|
||||||
struct TransferStorage { QPointer<FileStreamWriter> writer; };
|
struct TransferStorage { QPointer<FileStreamWriter> writer; };
|
||||||
Condition condition;
|
SingleBarrier writerReadyBarrier;
|
||||||
TreeStorage<TransferStorage> storage;
|
TreeStorage<TransferStorage> storage;
|
||||||
|
|
||||||
const auto setupReader = [=](FileStreamReader &reader) {
|
const auto setupReader = [=](FileStreamReader &reader) {
|
||||||
@@ -336,19 +337,19 @@ static Group interDeviceTransferTask(const FilePath &source, const FilePath &des
|
|||||||
};
|
};
|
||||||
const auto setupWriter = [=](FileStreamWriter &writer) {
|
const auto setupWriter = [=](FileStreamWriter &writer) {
|
||||||
writer.setFilePath(destination);
|
writer.setFilePath(destination);
|
||||||
ConditionActivator *activator = condition.activator();
|
|
||||||
QObject::connect(&writer, &FileStreamWriter::started,
|
QObject::connect(&writer, &FileStreamWriter::started,
|
||||||
&writer, [activator] { activator->activate(); });
|
writerReadyBarrier->barrier(), &Barrier::advance);
|
||||||
QTC_CHECK(storage->writer == nullptr);
|
QTC_CHECK(storage->writer == nullptr);
|
||||||
storage->writer = &writer;
|
storage->writer = &writer;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Group root {
|
const Group root {
|
||||||
|
Storage(writerReadyBarrier),
|
||||||
parallel,
|
parallel,
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
Writer(setupWriter),
|
Writer(setupWriter),
|
||||||
Group {
|
Group {
|
||||||
WaitFor(condition),
|
WaitForBarrier(writerReadyBarrier),
|
||||||
Reader(setupReader, finalizeReader, finalizeReader)
|
Reader(setupReader, finalizeReader, finalizeReader)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
#include <utils/asynctask.h>
|
#include <utils/asynctask.h>
|
||||||
|
#include <utils/barrier.h>
|
||||||
|
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ enum class Handler {
|
|||||||
GroupDone,
|
GroupDone,
|
||||||
GroupError,
|
GroupError,
|
||||||
Sync,
|
Sync,
|
||||||
Activator,
|
BarrierAdvance,
|
||||||
};
|
};
|
||||||
|
|
||||||
using Log = QList<QPair<int, Handler>>;
|
using Log = QList<QPair<int, Handler>>;
|
||||||
@@ -1147,63 +1148,65 @@ void tst_TaskTree::testTree_data()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Condition condition;
|
SingleBarrier barrier;
|
||||||
|
|
||||||
const auto reportAndSleep = [](QPromise<bool> &promise) {
|
const auto reportAndSleep = [](QPromise<bool> &promise) {
|
||||||
promise.addResult(false);
|
promise.addResult(false);
|
||||||
QThread::msleep(10);
|
QThread::msleep(10);
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto setupTaskWithCondition = [storage, condition, reportAndSleep](int taskId) {
|
const auto setupTaskWithBarrier = [storage, barrier, reportAndSleep](int taskId) {
|
||||||
return [storage, condition, reportAndSleep, taskId](AsyncTask<bool> &async) {
|
return [storage, barrier, reportAndSleep, taskId](AsyncTask<bool> &async) {
|
||||||
async.setFutureSynchronizer(s_futureSynchronizer);
|
async.setFutureSynchronizer(s_futureSynchronizer);
|
||||||
async.setConcurrentCallData(reportAndSleep);
|
async.setConcurrentCallData(reportAndSleep);
|
||||||
async.setProperty(s_taskIdProperty, taskId);
|
async.setProperty(s_taskIdProperty, taskId);
|
||||||
storage->m_log.append({taskId, Handler::Setup});
|
storage->m_log.append({taskId, Handler::Setup});
|
||||||
|
|
||||||
CustomStorage *currentStorage = storage.activeStorage();
|
CustomStorage *currentStorage = storage.activeStorage();
|
||||||
ConditionActivator *currentActivator = condition.activator();
|
Barrier *sharedBarrier = barrier->barrier();
|
||||||
connect(&async, &TestTask::resultReadyAt,
|
connect(&async, &TestTask::resultReadyAt, sharedBarrier,
|
||||||
[currentStorage, currentActivator, taskId](int index) {
|
[currentStorage, sharedBarrier, taskId](int index) {
|
||||||
Q_UNUSED(index)
|
Q_UNUSED(index)
|
||||||
currentStorage->m_log.append({taskId, Handler::Activator});
|
currentStorage->m_log.append({taskId, Handler::BarrierAdvance});
|
||||||
currentActivator->activate();
|
sharedBarrier->advance();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test that Activator, triggered from inside the task described by
|
// Test that barrier advance, triggered from inside the task described by
|
||||||
// setupTaskWithCondition, placed BEFORE the group containing the WaitFor element
|
// setupTaskWithCondition, placed BEFORE the group containing the waitFor() element
|
||||||
// in the tree order, works OK in SEQUENTIAL mode.
|
// in the tree order, works OK in SEQUENTIAL mode.
|
||||||
const Group root1 {
|
const Group root1 {
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
|
Storage(barrier),
|
||||||
sequential,
|
sequential,
|
||||||
Async<bool>(setupTaskWithCondition(1)),
|
Async<bool>(setupTaskWithBarrier(1)),
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(2)),
|
OnGroupSetup(groupSetup(2)),
|
||||||
WaitFor(condition),
|
WaitForBarrier(barrier),
|
||||||
Test(setupTask(2)),
|
Test(setupTask(2)),
|
||||||
Test(setupTask(3))
|
Test(setupTask(3))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const Log log1 {
|
const Log log1 {
|
||||||
{1, Handler::Setup},
|
{1, Handler::Setup},
|
||||||
{1, Handler::Activator},
|
{1, Handler::BarrierAdvance},
|
||||||
{2, Handler::GroupSetup},
|
{2, Handler::GroupSetup},
|
||||||
{2, Handler::Setup},
|
{2, Handler::Setup},
|
||||||
{3, Handler::Setup}
|
{3, Handler::Setup}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test that Activator, triggered from inside the task described by
|
// Test that barrier advance, triggered from inside the task described by
|
||||||
// setupTaskWithCondition, placed BEFORE the group containing the WaitFor element
|
// setupTaskWithCondition, placed BEFORE the group containing the waitFor() element
|
||||||
// in the tree order, works OK in PARALLEL mode.
|
// in the tree order, works OK in PARALLEL mode.
|
||||||
const Group root2 {
|
const Group root2 {
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
|
Storage(barrier),
|
||||||
parallel,
|
parallel,
|
||||||
Async<bool>(setupTaskWithCondition(1)),
|
Async<bool>(setupTaskWithBarrier(1)),
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(2)),
|
OnGroupSetup(groupSetup(2)),
|
||||||
WaitFor(condition),
|
WaitForBarrier(barrier),
|
||||||
Test(setupTask(2)),
|
Test(setupTask(2)),
|
||||||
Test(setupTask(3))
|
Test(setupTask(3))
|
||||||
}
|
}
|
||||||
@@ -1211,47 +1214,48 @@ void tst_TaskTree::testTree_data()
|
|||||||
const Log log2 {
|
const Log log2 {
|
||||||
{1, Handler::Setup},
|
{1, Handler::Setup},
|
||||||
{2, Handler::GroupSetup},
|
{2, Handler::GroupSetup},
|
||||||
{1, Handler::Activator},
|
{1, Handler::BarrierAdvance},
|
||||||
{2, Handler::Setup},
|
{2, Handler::Setup},
|
||||||
{3, Handler::Setup}
|
{3, Handler::Setup}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test that Activator, triggered from inside the task described by
|
// Test that barrier advance, triggered from inside the task described by
|
||||||
// setupTaskWithCondition, placed AFTER the group containing the WaitFor element
|
// setupTaskWithCondition, placed AFTER the group containing the waitFor() element
|
||||||
// in the tree order, works OK in PARALLEL mode.
|
// in the tree order, works OK in PARALLEL mode.
|
||||||
//
|
//
|
||||||
// Notice: This won't work in SEQUENTIAL mode, since the Activator placed after the
|
// Notice: This won't work in SEQUENTIAL mode, since the advancing barrier, placed after the
|
||||||
// group containing the WaitFor element, has no chance to be started in SEQUENTIAL mode,
|
// group containing the WaitFor element, has no chance to be started in SEQUENTIAL mode,
|
||||||
// as in SEQUENTIAL mode the next task may only be started after the previous one finished.
|
// as in SEQUENTIAL mode the next task may only be started after the previous one finished.
|
||||||
// In this case, the previous task (Group element) awaits for the Activator's signal to
|
// In this case, the previous task (Group element) awaits for the barrier's advance to
|
||||||
// come from the not yet started next task, causing a deadlock.
|
// come from the not yet started next task, causing a deadlock.
|
||||||
// The minimal requirement for this scenario to succeed is to set ParallelLimit(2) or more.
|
// The minimal requirement for this scenario to succeed is to set ParallelLimit(2) or more.
|
||||||
const Group root3 {
|
const Group root3 {
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
|
Storage(barrier),
|
||||||
parallel,
|
parallel,
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(2)),
|
OnGroupSetup(groupSetup(2)),
|
||||||
WaitFor(condition),
|
WaitForBarrier(barrier),
|
||||||
Test(setupTask(2)),
|
Test(setupTask(2)),
|
||||||
Test(setupTask(3))
|
Test(setupTask(3))
|
||||||
},
|
},
|
||||||
Async<bool>(setupTaskWithCondition(1))
|
Async<bool>(setupTaskWithBarrier(1))
|
||||||
};
|
};
|
||||||
const Log log3 {
|
const Log log3 {
|
||||||
{2, Handler::GroupSetup},
|
{2, Handler::GroupSetup},
|
||||||
{1, Handler::Setup},
|
{1, Handler::Setup},
|
||||||
{1, Handler::Activator},
|
{1, Handler::BarrierAdvance},
|
||||||
{2, Handler::Setup},
|
{2, Handler::Setup},
|
||||||
{3, Handler::Setup}
|
{3, Handler::Setup}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Notice the different log order for each scenario.
|
// Notice the different log order for each scenario.
|
||||||
QTest::newRow("WaitForSequential")
|
QTest::newRow("BarrierSequential")
|
||||||
<< TestData{storage, root1, log1, 3, OnStart::Running, OnDone::Success};
|
<< TestData{storage, root1, log1, 4, OnStart::Running, OnDone::Success};
|
||||||
QTest::newRow("WaitForParallelActivatorFirst")
|
QTest::newRow("BarrierParallelAdvanceFirst")
|
||||||
<< TestData{storage, root2, log2, 3, OnStart::Running, OnDone::Success};
|
<< TestData{storage, root2, log2, 4, OnStart::Running, OnDone::Success};
|
||||||
QTest::newRow("WaitForParallelConditionFirst")
|
QTest::newRow("BarrierParallelWaitForFirst")
|
||||||
<< TestData{storage, root3, log3, 3, OnStart::Running, OnDone::Success};
|
<< TestData{storage, root3, log3, 4, OnStart::Running, OnDone::Success};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user