mirror of https://github.com/linuxdeepin/dtkcore
parent
f5fd6cf9a8
commit
2df5ca70dd
|
@ -0,0 +1,55 @@
|
|||
/*!
|
||||
@~chinese
|
||||
@ingroup dutil
|
||||
@file include/util/dthreadutils.h
|
||||
|
||||
本文件定义了线程相关的帮助类
|
||||
|
||||
@class DThreadUtils
|
||||
@brief 线程帮助类
|
||||
@details 本类主要用来进行异步线程调用, 此外本类所有的public接口都是线程安全的
|
||||
|
||||
@fn static DThreadUtils& DThreadUtils::gui()
|
||||
@brief 获取以GUI线程初始化的静态对象
|
||||
@return DThreadUtils& 静态对象的引用
|
||||
|
||||
@fn QThread* DThreadUtils::thread() const noexcept
|
||||
@brief 获取DThreadUtils对应的线程
|
||||
@return QThread* 对应线程的QThread对象的指针
|
||||
|
||||
@fn template <typename Func, typename... Args> inline auto run(QObject *context,typename QtPrivate::FunctionPointer<Func>::Object *obj, Func fun, Args &&...args)
|
||||
@brief 在对应的线程执行传入的成员函数, 非阻塞
|
||||
@param[in] context 对象上下文, 用来在执行调用时判断对象是否存在
|
||||
@param[in] obj 对象指针
|
||||
@param[in] fun 成员函数指针
|
||||
@param[in] args 对应函数的参数
|
||||
@return 以成员函数返回值类型实例化的QFuture对象
|
||||
|
||||
@fn template <typename Func, typename... Args> inline auto run(typename QtPrivate::FunctionPointer<Func>::Object *obj, Func fun, Args &&...args)
|
||||
@brief 在对应的线程执行传入的成员函数, 非阻塞
|
||||
@param[in] obj 对象指针
|
||||
@param[in] fun 成员函数指针
|
||||
@param[in] args 对应函数的参数
|
||||
@return 以成员函数返回值类型实例化的QFuture对象
|
||||
|
||||
@fn template <typename Func, typename... Args> inline QFuture<std::invoke_result_t<std::decay_t<Func>, Args...>> run(QObject *context, Func fun, Args &&...args)
|
||||
@brief 在对应的线程执行传入的成员函数, 非阻塞
|
||||
@param[in] context 对象上下文, 用来在执行调用时判断对象是否存在
|
||||
@param[in] fun 成员函数指针
|
||||
@param[in] args 对应函数的参数
|
||||
@return 以成员函数返回值类型实例化的QFuture对象
|
||||
|
||||
@fn template <typename Func, typename... Args> inline QFuture<std::invoke_result_t<std::decay_t<Func>, Args...>> run(Func fun, Args &&...args)
|
||||
@brief 在对应的线程执行传入的成员函数, 非阻塞
|
||||
@param[in] fun 可调用对象
|
||||
@param[in] args 调用对应的参数
|
||||
@return 以函数返回值类型实例化的QFuture对象
|
||||
|
||||
@fn template <typename... T> inline decltype(auto) exec(T &&...args)
|
||||
@brief 在对应的线程执行传入的成员函数, 阻塞
|
||||
@details 本函数是run函数的包装
|
||||
@note 调用此函数的一方需要确保对应线程有事件循环, 否则会无限等待
|
||||
@param[in] args 参数包, 具体含义参考run函数
|
||||
@return 传入函数的返回值
|
||||
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd.
|
||||
// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
@ -13,8 +13,15 @@
|
|||
#include <QPointer>
|
||||
#include <QDebug>
|
||||
|
||||
#if DTK_VERSION >= DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||
#include <QFuture>
|
||||
#include <QPromise>
|
||||
#include <QEvent>
|
||||
#endif
|
||||
|
||||
DCORE_BEGIN_NAMESPACE
|
||||
|
||||
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||
namespace DThreadUtil {
|
||||
typedef std::function<void()> FunctionType;
|
||||
|
||||
|
@ -152,7 +159,170 @@ inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
|
|||
return runInMainThread(obj, obj, fun, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
#else
|
||||
class LIBDTKCORESHARED_EXPORT DThreadUtils final
|
||||
{
|
||||
friend class Caller;
|
||||
public:
|
||||
explicit DThreadUtils(QThread *thread);
|
||||
~DThreadUtils();
|
||||
|
||||
static DThreadUtils &gui();
|
||||
|
||||
QThread *thread() const noexcept;
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
inline auto run(QObject *context,typename QtPrivate::FunctionPointer<Func>::Object *obj, Func fun, Args &&...args)
|
||||
{
|
||||
return call(context, fun, *obj, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename Func, typename... Args>
|
||||
inline auto run(typename QtPrivate::FunctionPointer<Func>::Object *obj, Func fun, Args &&...args)
|
||||
{
|
||||
if constexpr (std::is_base_of<QObject, typename QtPrivate::FunctionPointer<Func>::Object>::value) {
|
||||
return call(obj, fun, *obj, std::forward<Args>(args)...);
|
||||
} else {
|
||||
return call(static_cast<QObject *>(nullptr), fun, *obj, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
template <typename Func, typename... Args>
|
||||
inline QFuture<std::invoke_result_t<std::decay_t<Func>, Args...>> run(QObject *context, Func fun, Args &&...args)
|
||||
{
|
||||
return call(context, fun, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename Func, typename... Args>
|
||||
inline QFuture<std::invoke_result_t<std::decay_t<Func>, Args...>> run(Func fun, Args &&...args)
|
||||
{
|
||||
return call(static_cast<QObject *>(nullptr), fun, std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename... T>
|
||||
inline decltype(auto) exec(T &&...args)
|
||||
{
|
||||
auto future = run(std::forward<T>(args)...);
|
||||
if (!thread()->isRunning()) {
|
||||
qWarning() << "The target thread is not running, maybe lead to deadlock.";
|
||||
}
|
||||
future.waitForFinished();
|
||||
if constexpr (std::is_same_v<decltype(future), QFuture<void>>) {
|
||||
return;
|
||||
} else {
|
||||
return future.result();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
class AbstractCallEvent : public QEvent
|
||||
{
|
||||
public:
|
||||
AbstractCallEvent(QEvent::Type type)
|
||||
: QEvent(type)
|
||||
{
|
||||
}
|
||||
virtual void call() = 0;
|
||||
};
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
class Q_DECL_HIDDEN CallEvent : public AbstractCallEvent
|
||||
{
|
||||
using FunInfo = QtPrivate::FunctionPointer<std::decay_t<Func>>;
|
||||
using ReturnType = std::invoke_result_t<std::decay_t<Func>, Args...>;
|
||||
|
||||
public:
|
||||
CallEvent(QEvent::Type type, Func &&fun, Args &&...args)
|
||||
: AbstractCallEvent(type)
|
||||
, function(std::forward<Func>(fun))
|
||||
, arguments(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
QEvent *clone() const override { return nullptr; }
|
||||
|
||||
void call() override
|
||||
{
|
||||
if (promise.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (contextChecker == context) {
|
||||
promise.start();
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
if constexpr (std::is_void_v<ReturnType>) {
|
||||
std::apply(function, arguments);
|
||||
} else {
|
||||
promise.addResult(std::apply(function, arguments));
|
||||
}
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
} catch (...) {
|
||||
promise.setException(std::current_exception());
|
||||
}
|
||||
#endif
|
||||
promise.finish();
|
||||
} else {
|
||||
promise.start();
|
||||
promise.setException(std::make_exception_ptr(std::runtime_error("The context object is destroyed.")));
|
||||
promise.finish();
|
||||
}
|
||||
}
|
||||
|
||||
Func function;
|
||||
const std::tuple<Args...> arguments;
|
||||
QPromise<ReturnType> promise;
|
||||
|
||||
QObject *context{nullptr};
|
||||
QPointer<QObject> contextChecker;
|
||||
};
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
inline auto call(QObject *context, Func fun, Args &&...args)
|
||||
{
|
||||
using FuncInfo = QtPrivate::FunctionPointer<std::decay_t<Func>>;
|
||||
using ReturnType = std::invoke_result_t<std::decay_t<Func>, Args...> ;
|
||||
|
||||
if constexpr (FuncInfo::IsPointerToMemberFunction) {
|
||||
static_assert(std::is_same_v<std::decay_t<typename QtPrivate::List<Args...>::Car>, typename FuncInfo::Object>,
|
||||
"The obj and function are not compatible.");
|
||||
static_assert(
|
||||
QtPrivate::CheckCompatibleArguments<typename QtPrivate::List<Args...>::Cdr, typename FuncInfo::Arguments>::value,
|
||||
"The args and function are not compatible.");
|
||||
} else if constexpr (FuncInfo::ArgumentCount != -1) {
|
||||
static_assert(QtPrivate::CheckCompatibleArguments<QtPrivate::List<Args...>, typename FuncInfo::Arguments>::value,
|
||||
"The args and function are not compatible.");
|
||||
} else { // for lambda and impl operator()
|
||||
static_assert(std::is_invocable_r_v<ReturnType, Func, Args...>,
|
||||
"The callable object can't invoke with supplied args");
|
||||
}
|
||||
|
||||
QPromise<ReturnType> promise;
|
||||
auto future = promise.future();
|
||||
|
||||
if (Q_UNLIKELY(QThread::currentThread() == m_thread)) {
|
||||
promise.start();
|
||||
if constexpr (std::is_void_v<ReturnType>) {
|
||||
std::invoke(fun, std::forward<Args>(args)...);
|
||||
} else {
|
||||
promise.addResult(std::invoke(fun, std::forward<Args>(args)...));
|
||||
}
|
||||
promise.finish();
|
||||
} else {
|
||||
auto event = new CallEvent<Func, Args...>(eventType, std::move(fun), std::forward<Args>(args)...);
|
||||
event->promise = std::move(promise);
|
||||
event->context = context;
|
||||
event->contextChecker = context;
|
||||
|
||||
QCoreApplication::postEvent(ensureThreadContextObject(), event);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
QObject *ensureThreadContextObject();
|
||||
|
||||
static inline QEvent::Type eventType;
|
||||
QThread *m_thread;
|
||||
QAtomicPointer<QObject> threadContext;
|
||||
};
|
||||
#endif // version macro end
|
||||
DCORE_END_NAMESPACE
|
||||
|
||||
#endif // DTHREADUTILS_H
|
||||
#endif // protect macro end
|
||||
|
|
|
@ -66,12 +66,12 @@ if(LINUX)
|
|||
target_link_libraries(
|
||||
${LIB_NAME} PUBLIC
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::CorePrivate
|
||||
Qt${QT_VERSION_MAJOR}::DBus
|
||||
Qt${QT_VERSION_MAJOR}::Xml
|
||||
)
|
||||
target_link_libraries(${LIB_NAME} PRIVATE
|
||||
ICU::uc
|
||||
Qt${QT_VERSION_MAJOR}::CorePrivate
|
||||
uchardet
|
||||
)
|
||||
if("${QT_VERSION_MAJOR}" STREQUAL "5")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd.
|
||||
// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
|||
|
||||
DCORE_BEGIN_NAMESPACE
|
||||
|
||||
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||
namespace DThreadUtil {
|
||||
FunctionCallProxy::FunctionCallProxy(QThread *thread)
|
||||
{
|
||||
|
@ -41,6 +42,67 @@ void FunctionCallProxy::proxyCall(QSemaphore *s, QThread *thread, QObject *targe
|
|||
proxy.callInLiveThread(s, target ? target : &proxy, &fun);
|
||||
s->acquire();
|
||||
}
|
||||
} // end namespace DThreadUtil
|
||||
|
||||
}
|
||||
#else
|
||||
class Q_DECL_HIDDEN Caller : public QObject
|
||||
{
|
||||
public:
|
||||
explicit Caller()
|
||||
: QObject()
|
||||
{
|
||||
}
|
||||
|
||||
bool event(QEvent *event) override
|
||||
{
|
||||
if (event->type() == DThreadUtils::eventType) {
|
||||
auto ev = static_cast<DThreadUtils::AbstractCallEvent *>(event);
|
||||
ev->call();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QObject::event(event);
|
||||
}
|
||||
};
|
||||
|
||||
DThreadUtils::DThreadUtils(QThread *thread)
|
||||
: m_thread(thread)
|
||||
, threadContext(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DThreadUtils::~DThreadUtils()
|
||||
{
|
||||
delete threadContext.loadRelaxed();
|
||||
}
|
||||
|
||||
DThreadUtils &DThreadUtils::gui()
|
||||
{
|
||||
static auto global = DThreadUtils(QCoreApplication::instance()->thread());
|
||||
return global;
|
||||
}
|
||||
|
||||
QThread *DThreadUtils::thread() const noexcept
|
||||
{
|
||||
return m_thread;
|
||||
}
|
||||
|
||||
QObject *DThreadUtils::ensureThreadContextObject()
|
||||
{
|
||||
QObject *context;
|
||||
if (!threadContext.loadRelaxed()) {
|
||||
context = new Caller();
|
||||
context->moveToThread(m_thread);
|
||||
if (!threadContext.testAndSetRelaxed(nullptr, context)) {
|
||||
context->moveToThread(nullptr);
|
||||
delete context;
|
||||
}
|
||||
}
|
||||
|
||||
context = threadContext.loadRelaxed();
|
||||
Q_ASSERT(context);
|
||||
|
||||
return context;
|
||||
}
|
||||
#endif
|
||||
DCORE_END_NAMESPACE
|
||||
|
|
|
@ -40,10 +40,17 @@ else()
|
|||
)
|
||||
endif()
|
||||
file(GLOB UTILS_HEADER
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../include/util/*
|
||||
${PROJECT_SOURCE_DIR}/include/util/*.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ddbusinterface_p.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher_p.h
|
||||
)
|
||||
|
||||
if("${QT_VERSION_MAJOR}" STREQUAL "6")
|
||||
list(REMOVE_ITEM UTILS_SOURCE "${CMAKE_CURRENT_LIST_DIR}/dtimedloop.cpp")
|
||||
list(REMOVE_ITEM UTILS_HEADER "${PROJECT_SOURCE_DIR}/include/util/dtimedloop.h") # no longer be used
|
||||
list(REMOVE_ITEM UTILS_HEADER "${PROJECT_SOURCE_DIR}/include/util/dasync.h")
|
||||
endif()
|
||||
|
||||
set(utils_SRC
|
||||
${UTILS_HEADER}
|
||||
${UTILS_SOURCE}
|
||||
|
|
|
@ -80,6 +80,7 @@ file(GLOB FackDBus
|
|||
|
||||
if("${QT_VERSION_MAJOR}" STREQUAL "6")
|
||||
list(REMOVE_ITEM TEST_SOURCE "${CMAKE_CURRENT_LIST_DIR}/ut_gsettingsbackend.cpp")
|
||||
list(REMOVE_ITEM TEST_SOURCE "${CMAKE_CURRENT_LIST_DIR}/ut_dasync.cpp")
|
||||
endif()
|
||||
|
||||
set(test_SRC
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
|
||||
// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
@ -14,7 +14,9 @@ int main(int argc, char *argv[])
|
|||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationName("tests");
|
||||
app.setOrganizationName("deepin");
|
||||
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||
DTimedLoop loop;
|
||||
#endif
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
int retVal = RUN_ALL_TESTS();
|
||||
|
@ -23,5 +25,9 @@ int main(int argc, char *argv[])
|
|||
__sanitizer_set_report_path("asan.log");
|
||||
#endif
|
||||
|
||||
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||
return loop.exec(0, "main execution") + retVal;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd.
|
||||
// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
|
@ -6,11 +6,16 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <QTest>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <DThreadUtils>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
DCORE_USE_NAMESPACE
|
||||
|
||||
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||
|
||||
class ThreadUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -59,4 +64,198 @@ TEST_F(ut_DThreadUtils, CallInMainThread)
|
|||
m_threadutil->testCallInMainThread();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
class ut_DThreadUtils :public testing::Test{
|
||||
public:
|
||||
virtual void SetUp()
|
||||
{
|
||||
t = new QThread();
|
||||
t->start();
|
||||
m_threadutil = new DThreadUtils(t);
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
t->exit();
|
||||
t->wait();
|
||||
delete t;
|
||||
delete m_threadutil;
|
||||
}
|
||||
|
||||
protected:
|
||||
QThread *t{nullptr};
|
||||
DThreadUtils *m_threadutil{nullptr};
|
||||
};
|
||||
|
||||
class QWorker : public QObject{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWorker(QObject *parent = nullptr):QObject(parent){}
|
||||
~QWorker() = default;
|
||||
public Q_SLOTS:
|
||||
int testFunc(int i, double j) {
|
||||
int r = i + j;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
emit testFuncTrigger(r);
|
||||
return i + j;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void testFuncTrigger(int v);
|
||||
};
|
||||
|
||||
class CallableObject{
|
||||
|
||||
public:
|
||||
CallableObject() = default;
|
||||
~CallableObject() = default;
|
||||
|
||||
QString operator()(const QString& str){
|
||||
s += str;
|
||||
return s;
|
||||
}
|
||||
|
||||
QString testFunc(const QString& str){
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
QString s{"CallableObject: "};
|
||||
};
|
||||
|
||||
TEST_F(ut_DThreadUtils,testThread)
|
||||
{
|
||||
auto tmp = m_threadutil->thread();
|
||||
EXPECT_EQ(tmp, t);
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils, testRunWithQObj)
|
||||
{
|
||||
QWorker w;
|
||||
QSignalSpy spy(&w,SIGNAL(testFuncTrigger(int)));
|
||||
auto result = m_threadutil->run(&w, &QWorker::testFunc, 10, 24.6);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
EXPECT_TRUE(result.isStarted());
|
||||
EXPECT_TRUE(result.isRunning());
|
||||
result.waitForFinished();
|
||||
EXPECT_TRUE(result.isFinished());
|
||||
auto raw = result.result();
|
||||
EXPECT_EQ(raw, 34);
|
||||
EXPECT_EQ(spy.count(), 1);
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils,testRunWithLambda)
|
||||
{
|
||||
const QString& ref = "long ref";
|
||||
int num = 10;
|
||||
auto threadId1 = std::this_thread::get_id();
|
||||
auto result = m_threadutil->run([&num](decltype(threadId1) id){
|
||||
EXPECT_NE(std::this_thread::get_id(),id);
|
||||
return true;
|
||||
},threadId1);
|
||||
result.waitForFinished();
|
||||
auto raw = result.result();
|
||||
EXPECT_TRUE(raw);
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils,testRunWithCallableObj){
|
||||
CallableObject obj;
|
||||
QString tmp{"Hello"};
|
||||
const auto& ref = tmp;
|
||||
auto result1 = m_threadutil->run(obj,tmp);
|
||||
result1.waitForFinished();
|
||||
auto raw1 = result1.result();
|
||||
EXPECT_EQ(raw1, QString{"CallableObject: Hello"});
|
||||
|
||||
auto result2 = m_threadutil->run(&obj,&CallableObject::testFunc, tmp);
|
||||
result2.waitForFinished();
|
||||
auto raw2 = result2.result();
|
||||
EXPECT_EQ(raw2, QString{"CallableObject: "});
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils, testExecWithQObj)
|
||||
{
|
||||
QWorker w;
|
||||
QSignalSpy spy(&w, SIGNAL(testFuncTrigger(int)));
|
||||
auto result = m_threadutil->exec(&w, &QWorker::testFunc, 10, 24.6);
|
||||
EXPECT_EQ(result, 34);
|
||||
EXPECT_EQ(spy.count(), 1);
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils, testExecWithLambda)
|
||||
{
|
||||
const QString &ref = "long ref";
|
||||
int num = 10;
|
||||
auto threadId1 = std::this_thread::get_id();
|
||||
auto result = m_threadutil->exec(
|
||||
[&num](decltype(threadId1) id) {
|
||||
EXPECT_NE(std::this_thread::get_id(), id);
|
||||
return true;
|
||||
},
|
||||
threadId1);
|
||||
EXPECT_TRUE(result);
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils, testExecWithCallableObj)
|
||||
{
|
||||
CallableObject obj;
|
||||
QString tmp{"Hello"};
|
||||
const auto &ref = tmp;
|
||||
auto result1 = m_threadutil->exec(obj, tmp);
|
||||
EXPECT_EQ(result1, QString{"CallableObject: Hello"});
|
||||
|
||||
auto result2 = m_threadutil->exec(&obj, &CallableObject::testFunc, tmp);
|
||||
EXPECT_EQ(result2, QString{"CallableObject: "});
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils, testDirectlyInvoke)
|
||||
{
|
||||
DThreadUtils tu(QThread::currentThread());
|
||||
QWorker w;
|
||||
QSignalSpy spy(&w, SIGNAL(testFuncTrigger(int)));
|
||||
auto result = tu.run(&w, &QWorker::testFunc, 10, 24.6);
|
||||
auto raw = result.result(); // no wait
|
||||
EXPECT_EQ(raw, 34);
|
||||
EXPECT_EQ(spy.count(), 1);
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils, testCancel)
|
||||
{
|
||||
CallableObject obj;
|
||||
QString tmp{"Hello"};
|
||||
int cancelCounter{0};
|
||||
const auto &ref = tmp;
|
||||
auto result1 = m_threadutil->run(obj, tmp);
|
||||
auto cancelResult = result1.onCanceled([&cancelCounter]() {
|
||||
cancelCounter += 1;
|
||||
return QString{"failed"};
|
||||
});
|
||||
result1.cancel();
|
||||
EXPECT_FALSE(result1.isFinished());
|
||||
EXPECT_FALSE(result1.isValid());
|
||||
EXPECT_TRUE(result1.isCanceled());
|
||||
cancelResult.waitForFinished();
|
||||
EXPECT_EQ(cancelCounter, 1);
|
||||
EXPECT_EQ(cancelResult.result(), QString{"failed"});
|
||||
}
|
||||
|
||||
TEST_F(ut_DThreadUtils, testDestructCancel)
|
||||
{
|
||||
auto w = new QWorker{};
|
||||
auto failedCounter{0};
|
||||
auto result = m_threadutil->run(w, &QWorker::testFunc, 10, 24.6);
|
||||
delete w;
|
||||
auto failedResult = result.onFailed([&failedCounter]() {
|
||||
failedCounter += 1;
|
||||
return -1;
|
||||
});
|
||||
EXPECT_FALSE(result.isValid());
|
||||
failedResult.waitForFinished();
|
||||
EXPECT_EQ(failedCounter, 1);
|
||||
EXPECT_EQ(failedResult.result(), -1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "ut_dthreadutils.moc"
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
// SPDX-FileCopyrightText: 2017 - 2022 UnionTech Software Technology Co., Ltd.
|
||||
// SPDX-FileCopyrightText: 2017 - 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dtkcore_global.h"
|
||||
#include <gtest/gtest.h>
|
||||
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||
#include "dtimedloop.h"
|
||||
|
||||
DCORE_USE_NAMESPACE
|
||||
#endif
|
||||
|
||||
// 有返回值的 lambda 表达式、函数里面使用 ASSERT_XXX
|
||||
// 总之,不管有没有返回值,用它就对了
|
||||
|
|
Loading…
Reference in New Issue