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
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -13,8 +13,15 @@
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
#if DTK_VERSION >= DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QPromise>
|
||||||
|
#include <QEvent>
|
||||||
|
#endif
|
||||||
|
|
||||||
DCORE_BEGIN_NAMESPACE
|
DCORE_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||||
namespace DThreadUtil {
|
namespace DThreadUtil {
|
||||||
typedef std::function<void()> FunctionType;
|
typedef std::function<void()> FunctionType;
|
||||||
|
|
||||||
|
@ -152,7 +159,170 @@ inline typename QtPrivate::FunctionPointer<Fun>::ReturnType
|
||||||
return runInMainThread(obj, obj, fun, std::forward<Args>(args)...);
|
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
|
DCORE_END_NAMESPACE
|
||||||
|
#endif // protect macro end
|
||||||
#endif // DTHREADUTILS_H
|
|
||||||
|
|
|
@ -66,12 +66,12 @@ if(LINUX)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${LIB_NAME} PUBLIC
|
${LIB_NAME} PUBLIC
|
||||||
Qt${QT_VERSION_MAJOR}::Core
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::CorePrivate
|
||||||
Qt${QT_VERSION_MAJOR}::DBus
|
Qt${QT_VERSION_MAJOR}::DBus
|
||||||
Qt${QT_VERSION_MAJOR}::Xml
|
Qt${QT_VERSION_MAJOR}::Xml
|
||||||
)
|
)
|
||||||
target_link_libraries(${LIB_NAME} PRIVATE
|
target_link_libraries(${LIB_NAME} PRIVATE
|
||||||
ICU::uc
|
ICU::uc
|
||||||
Qt${QT_VERSION_MAJOR}::CorePrivate
|
|
||||||
uchardet
|
uchardet
|
||||||
)
|
)
|
||||||
if("${QT_VERSION_MAJOR}" STREQUAL "5")
|
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
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
DCORE_BEGIN_NAMESPACE
|
DCORE_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||||
namespace DThreadUtil {
|
namespace DThreadUtil {
|
||||||
FunctionCallProxy::FunctionCallProxy(QThread *thread)
|
FunctionCallProxy::FunctionCallProxy(QThread *thread)
|
||||||
{
|
{
|
||||||
|
@ -41,6 +42,67 @@ void FunctionCallProxy::proxyCall(QSemaphore *s, QThread *thread, QObject *targe
|
||||||
proxy.callInLiveThread(s, target ? target : &proxy, &fun);
|
proxy.callInLiveThread(s, target ? target : &proxy, &fun);
|
||||||
s->acquire();
|
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
|
DCORE_END_NAMESPACE
|
||||||
|
|
|
@ -40,10 +40,17 @@ else()
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
file(GLOB UTILS_HEADER
|
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}/ddbusinterface_p.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ddbusextendedpendingcallwatcher_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
|
set(utils_SRC
|
||||||
${UTILS_HEADER}
|
${UTILS_HEADER}
|
||||||
${UTILS_SOURCE}
|
${UTILS_SOURCE}
|
||||||
|
|
|
@ -80,6 +80,7 @@ file(GLOB FackDBus
|
||||||
|
|
||||||
if("${QT_VERSION_MAJOR}" STREQUAL "6")
|
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_gsettingsbackend.cpp")
|
||||||
|
list(REMOVE_ITEM TEST_SOURCE "${CMAKE_CURRENT_LIST_DIR}/ut_dasync.cpp")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(test_SRC
|
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
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -14,7 +14,9 @@ int main(int argc, char *argv[])
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
app.setApplicationName("tests");
|
app.setApplicationName("tests");
|
||||||
app.setOrganizationName("deepin");
|
app.setOrganizationName("deepin");
|
||||||
|
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||||
DTimedLoop loop;
|
DTimedLoop loop;
|
||||||
|
#endif
|
||||||
|
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
int retVal = RUN_ALL_TESTS();
|
int retVal = RUN_ALL_TESTS();
|
||||||
|
@ -23,5 +25,9 @@ int main(int argc, char *argv[])
|
||||||
__sanitizer_set_report_path("asan.log");
|
__sanitizer_set_report_path("asan.log");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||||
return loop.exec(0, "main execution") + retVal;
|
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
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
@ -6,11 +6,16 @@
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
#include <QSignalSpy>
|
||||||
#include <DThreadUtils>
|
#include <DThreadUtils>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
DCORE_USE_NAMESPACE
|
DCORE_USE_NAMESPACE
|
||||||
|
|
||||||
|
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||||
|
|
||||||
class ThreadUtils : public QObject
|
class ThreadUtils : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -59,4 +64,198 @@ TEST_F(ut_DThreadUtils, CallInMainThread)
|
||||||
m_threadutil->testCallInMainThread();
|
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"
|
#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
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "dtkcore_global.h"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
||||||
#include "dtimedloop.h"
|
#include "dtimedloop.h"
|
||||||
|
|
||||||
DCORE_USE_NAMESPACE
|
DCORE_USE_NAMESPACE
|
||||||
|
#endif
|
||||||
|
|
||||||
// 有返回值的 lambda 表达式、函数里面使用 ASSERT_XXX
|
// 有返回值的 lambda 表达式、函数里面使用 ASSERT_XXX
|
||||||
// 总之,不管有没有返回值,用它就对了
|
// 总之,不管有没有返回值,用它就对了
|
||||||
|
|
Loading…
Reference in New Issue