Continues migration with DDS, Common

This commit is contained in:
torsten committed 2026-07-17 12:41:42 +02:00
1 parent 9b15cee841
commit fa48db111e
668 files changed
+111841 -2

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# ddsftc
ADD_LIBRARY(ddsftc INTERFACE)
TARGET_INCLUDE_DIRECTORIES(ddsftc
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/../../
)
TARGET_LINK_LIBRARIES(ddsftc
INTERFACE
Qt5::Core
ddsfts_connext
ddscore # due to the usage of Core::FtReplyState inside generic_ftc_impl.h, only on core.hpp - fixme :/
ddsgen
ddsQoS
RTI::nddsc
RTI::nddscpp2
ddsftccore
LtsUtilsCoreHeaderOnly
)
SET(SRCS
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc.h
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc_impl.h
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc_threading.h
${CMAKE_CURRENT_SOURCE_DIR}/generic_ftc_threading.cpp
)
TARGET_SOURCES(ddsftc INTERFACE ${SRCS})
# ddsftccore
NGL_ADD_LIBRARY2(WS SV HLA ddsftccore SHARED abstract_requester.h abstract_requester.cpp)
NGL_TARGET_INCLUDE_DIRECTORIES(ddsftccore
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../../ # 'DDS/FTC/...' reads better than only FTC
)
NGL_TARGET_LINK_LIBRARIES(ddsftccore
PUBLIC
Qt5::Core
ddscore
)
NGL_INSTALL_TARGET(ddsftccore)
+6
View File
@@ -0,0 +1,6 @@
#include "abstract_requester.h"
Request2::Request2():QObject(){}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <DDS/FTC/ddsftccore_export.h>
#include <QObject>
class DDSFTCCORE_EXPORT Request2 : public QObject
{
Q_OBJECT
public:
explicit Request2();
signals:
void finished();
};
+5
View File
@@ -0,0 +1,5 @@
#include "generic_ftc.h"
namespace FTC
{
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <DDS/Core/core.hpp>
#include <future>
class Request2;
namespace FTC
{
template <typename RequestTopicType, typename ReplyTopicType>
class GenericFtc
{
public:
GenericFtc() = default;
virtual ~GenericFtc() = default;
virtual bool requestDataClassic(const RequestTopicType &data,
const char *replySlot,
const char *errorSlot,
int &uniqueId) = 0;
virtual std::future<ReplyTopicType> requestDataModern(const RequestTopicType &requestTopic, Request2 *requestWatcher) = 0;
};
}
+567
View File
@@ -0,0 +1,567 @@
#pragma once
#include "generic_ftc.h"
#include "abstract_requester.h"
#include "generic_ftc_threading.h"
#include <DDS/Core/core.hpp>
#include <DDS/FTS/connext.h>
#include <DDS/Generic/generic_logger.h>
#include <DDS/Generic/generic_base.h>
#include <DDS/QosProfile/DDS_DefaultQos.h>
#include <QObject>
#include <QMetaMethod>
#include <QTimer>
#include <queue>
#include <future>
namespace FTC
{
template <class C>
class HasStatusFunction
{
template <class T>
static std::true_type testSignature(void (T::*)(const Core::FtReplyState &));
template <class T>
static decltype(testSignature(&T::replyState)) test(std::nullptr_t);
template <class T>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
template <class C>
class HasRequestId
{
template <class T>
static std::true_type testSignature(const int32_t &(T::*)() const);
template <class T>
static std::true_type testSignature(int32_t (T::*)() const);
template <class T>
static decltype(testSignature(&T::requestId)) test(std::nullptr_t);
template <class T>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
template<typename GENERIC_FTC>
struct TopicNames
{};
template<typename TOPICTYPE_REQUEST, typename TOPICTYPE_REPLY, typename DUMMY>
class GenericFtcImpl final : public GenericFtc<TOPICTYPE_REQUEST, TOPICTYPE_REPLY>, public Threading
{
public:
using RequestTopic = TOPICTYPE_REQUEST;
using ReplyTopic = TOPICTYPE_REPLY;
using TopicNameHelper = TopicNames<GenericFtcImpl<RequestTopic, ReplyTopic, DUMMY>>;
GenericFtcImpl(const dds::domain::DomainParticipant &domainParticipant,
const dds::pub::Publisher &ddsPublisher,
const dds::sub::Subscriber &ddsSubscriber,
QObject *objToInform,
const std::string &partitionType,
int partitionId)
: GenericFtc<RequestTopic, ReplyTopic>()
, m_objToInform(objToInform)
, m_requesterParams(domainParticipant)
, m_partitionType(partitionType)
, m_partitionId(partitionId)
, m_logger(DDS::getLogger(TopicNameHelper::functionTopicName(), DDS::DataType::FUNCTION_TOPIC_CLIENT))
{
Q_ASSERT(domainParticipant != nullptr);
Q_ASSERT(ddsPublisher != nullptr);
Q_ASSERT(ddsSubscriber != nullptr);
Q_ASSERT(objToInform != nullptr);
m_requesterParams.request_topic_name(TopicNameHelper::requestTopicName());
m_requesterParams.reply_topic_name(TopicNameHelper::replyTopicName());
m_requesterParams.publisher(ddsPublisher);
m_requesterParams.subscriber(ddsSubscriber);
m_requesterParams.datareader_qos(DDS::QosProfile::dataReaderDefault(DDS::QosProfile::TopicType::FunctionTopic));
m_requesterParams.datawriter_qos(DDS::QosProfile::dataWriterDefault(DDS::QosProfile::TopicType::FunctionTopic));
DDS::QosProfile::print(m_requesterParams.datareader_qos(), m_logger);
DDS::QosProfile::print(m_requesterParams.datawriter_qos(), m_logger);
DEBUG_LOG(m_logger) << "=> Static request using request/reply topic names: "
<< TopicNameHelper::requestTopicName()
<< ", reply: "
<< TopicNameHelper::replyTopicName();
qRegisterMetaType<ReplyTopic>(TopicNameHelper::replyTopicTypeName());
preInit();
}
~GenericFtcImpl()
{
Q_ASSERT(!m_dying);
m_dying = true;
decltype(m_modernRequests) modernRequests;
decltype(m_classicRequests) classicRequests;
{
{
std::lock_guard<std::mutex> l(m_conditionMutex);
setWorkAvailable(false);
}
std::lock_guard<std::mutex> l(m_requestMutex);
Q_ASSERT(m_classicRequests.empty());
Q_ASSERT(m_modernRequests.empty());
classicRequests.swap(m_classicRequests);
modernRequests.swap(m_modernRequests);
}
for (; !modernRequests.empty(); modernRequests.pop())
{
ModernRequest<ReplyTopic> &request = modernRequests.front();
fprintf(stderr, "[!] GenericFtcImpl::dtor: there was a modern request in the queue while the Ftc is being destroyed. Throwing!\n");
Q_ASSERT(false);
request.promise.set_exception(std::make_exception_ptr(std::runtime_error("Session aborted.")));
emit request.requestWatcher->finished();
}
for (; !classicRequests.empty(); classicRequests.pop())
{
ClassicRequest &request = classicRequests.front();
fprintf(stderr, "[!] GenericFtcImpl::dtor: there was a classic request in the queue while the Ftc is being destroyed. Throwing!\n");
Q_ASSERT(false);
sendError(request, 0, true );
}
}
struct ClassicRequest
{
connext::WriteSample<RequestTopic> sample;
QMetaMethod replySlot;
QMetaMethod errorSlot;
int uniqueId = 0;
};
bool requestDataClassic(const RequestTopic &requestTopic,
const char *replySlot,
const char *errorSlot,
int &uniqueId) override
{
Q_ASSERT(!m_dying);
ClassicRequest request;
if (!checkSlots(request, replySlot, errorSlot))
return false;
request.sample.set_data(requestTopic);
request.uniqueId = ++m_uniqueId;
uniqueId = request.uniqueId;
createThread();
{
std::lock_guard<std::mutex> l(m_requestMutex);
m_classicRequests.push(request);
}
runThread();
return true;
}
template<typename ReplyTopic>
struct ModernRequest
{
std::promise<ReplyTopic> promise;
connext::WriteSample<RequestTopic> sample;
Request2 *requestWatcher;
};
std::future<ReplyTopic> requestDataModern(const RequestTopic &requestTopic, Request2 *requestWatcher) override
{
Q_ASSERT(!m_dying);
std::promise<ReplyTopic> promise;
std::future<ReplyTopic> future = promise.get_future();
connext::WriteSample<RequestTopic> sample;
sample.set_data(requestTopic);
createThread();
{
std::lock_guard<std::mutex> l(m_requestMutex);
m_modernRequests.push({ std::move(promise), sample, requestWatcher });
}
runThread();
return future;
}
private:
QString functionTopicName() const override
{
return QString::fromUtf8(TopicNameHelper::functionTopicName());
}
int partitionId() const override
{
return m_partitionId;
}
QString partitionType() const override
{
return QString::fromStdString(m_partitionType);
}
std::unique_ptr<connext::Requester<RequestTopic, ReplyTopic> > requester;
void cleanupThread() override
{
requester.reset();
}
void threadDone() override
{
for (; !m_modernRequests.empty(); m_modernRequests.pop())
{
ModernRequest<ReplyTopic> &request = m_modernRequests.front();
fprintf(stderr, "[!] GenericFtcImpl::threadDone: there was a modern request in the queue while the thread already ended. Throwing!\n");
Q_ASSERT(false);
request.promise.set_exception(std::make_exception_ptr(std::runtime_error("Worker thread timed out.")));
}
for (; !m_classicRequests.empty(); m_classicRequests.pop())
{
ClassicRequest &request = m_classicRequests.front();
fprintf(stderr, "[!] GenericFtcImpl::dtor: there was a classic request in the queue while the thread already ended. Throwing!\n");
Q_ASSERT(false);
sendError(request, 0, true );
}
}
void work() override
{
try
{
decltype(m_classicRequests) classicRequests;
decltype(m_modernRequests) modernRequests;
Q_ASSERT(isWorkAvailable());
if (!isWorkAvailable())
{
fprintf(stderr, "[!] GenericFtc::work: called while marked as no work available! run? %d (%s)\n", isRun(), threadName().toStdString().c_str());
}
setWorkAvailable(false);
{
std::lock_guard<std::mutex> l(m_requestMutex);
if (!m_classicRequests.empty())
{
m_classicRequests.swap(classicRequests);
}
if (!m_modernRequests.empty())
{
m_modernRequests.swap(modernRequests);
}
}
cancellationPoint();
if (classicRequests.empty() && modernRequests.empty())
{
fprintf(stderr, "[!] GenericFtc::work: called without having any requests! (%s)\n", threadName().toStdString().c_str());
Q_ASSERT(false);
return;
}
if (!requester)
{
requester.reset(new connext::Requester<RequestTopic, ReplyTopic>(m_requesterParams));
}
for (; !classicRequests.empty(); classicRequests.pop())
{
cancellationPoint();
ClassicRequest &request = classicRequests.front();
requester->send_request(request.sample);
cancellationPoint();
getRequestedDataClassic(*requester.get(), request);
cancellationPoint();
}
for (; !modernRequests.empty(); modernRequests.pop())
{
cancellationPoint();
ModernRequest<ReplyTopic> &request = modernRequests.front();
requester->send_request(request.sample);
cancellationPoint();
try
{
getModernRequestedData(*requester.get(), request);
}
catch (...)
{
request.promise.set_exception(std::current_exception());
QTimer::singleShot(0, request.requestWatcher, &Request2::finished);
}
cancellationPoint();
}
}
catch (const dds::core::Exception &ex)
{
fprintf(stderr, "[!] GenericFtc::work: dds::core::exception: %s (%s)\n", ex.what(), threadName().toStdString().c_str());
}
catch (const std::exception &ex)
{
fprintf(stderr, "[!] GenericFtc::work: exception: %s (%s)\n", ex.what(), threadName().toStdString().c_str());
}
catch(...)
{
fprintf(stderr, "[!] GenericFtc::work: unknown exception occured !!! (%s)\n", threadName().toStdString().c_str());
}
}
template <typename T, typename std::enable_if<HasStatusFunction<T>::value, int>::type = 0>
static inline bool isInProgress(const connext::Sample<T> &reply)
{
return (reply.data().replyState() == Core::FtReplyState::InProgress);
}
template <typename T, typename std::enable_if<!HasStatusFunction<T>::value, int>::type = 0>
static inline bool isInProgress(const connext::Sample<T> &)
{
return false;
}
void getRequestedDataClassic(connext::Requester<RequestTopic, ReplyTopic> &requester, ClassicRequest &request)
{
#ifdef WIN32
const dds::core::Duration MAX_WAIT(200);
#else
const dds::core::Duration MAX_WAIT(20);
#endif
bool inProgress = true;
while (inProgress && isRun())
{
DEBUG_LOG(m_logger) << "Waiting for reply ";
connext::Sample<ReplyTopic> reply;
if (!requester.receive_reply(reply, MAX_WAIT))
{
WARNING_LOG(m_logger) << "Timed out waiting for reply";
sendError(request, 3);
return;
}
if (reply.info().valid())
{
if (request.sample.info().identity() != reply.info()->related_original_publication_virtual_sample_identity())
{
INFO_LOG(m_logger) << "#receiveReply - outdated. sample id: " << connext::DebugInfo::toReadable(request.sample.info().identity())
<< ", virutal sample id: " << connext::DebugInfo::toReadable(reply.info()->related_original_publication_virtual_sample_identity());
continue;
}
inProgress = isInProgress(reply);
if (request.replySlot.isValid())
{
INFO_LOG(m_logger) << "#receiveReply for " << TopicNameHelper::replyTopicTypeName();
request.replySlot.invoke(m_objToInform, Qt::QueuedConnection,
QArgument<ReplyTopic>(TopicNameHelper::replyTopicTypeName(), reply.data()),
Q_ARG(int, request.uniqueId),
Q_ARG(bool, !inProgress));
}
else
{
WARNING_LOG(m_logger) << "reply slot doesn't exist for " << dds::topic::topic_type_name<ReplyTopic>::value();
}
if (!inProgress)
return;
}
else
WARNING_LOG(m_logger) << "No valid data available ";
}
}
void getModernRequestedData(connext::Requester<RequestTopic, ReplyTopic> &requester, ModernRequest<ReplyTopic> &request) noexcept(false)
{
#ifdef WIN32
const dds::core::Duration MAX_WAIT(200);
#else
const dds::core::Duration MAX_WAIT(20);
#endif
while (isRun())
{
connext::Sample<ReplyTopic> reply;
if (!requester.receive_reply(reply, MAX_WAIT))
{
WARNING_LOG(m_logger) << "Timeout while waiting for reply!";
throw std::runtime_error("Timeout while waiting for reply!");
}
if (!reply.info().valid())
{
WARNING_LOG(m_logger) << "No valid data available!";
throw std::runtime_error("No valid data available!");
}
if (request.sample.info().identity() != reply.info()->related_original_publication_virtual_sample_identity())
{
INFO_LOG(m_logger) << "#receiveReply - outdated. sample id: " << connext::DebugInfo::toReadable(request.sample.info().identity())
<< ", virutal sample id: " << connext::DebugInfo::toReadable(reply.info()->related_original_publication_virtual_sample_identity());
continue;
}
Q_ASSERT(!isInProgress(reply));
if (isInProgress(reply))
{
ERROR_LOG(m_logger) << "Used ModernRequest for a request with FtReplyState! Your data will most likely be imcomplete!";
}
INFO_LOG(m_logger) << "#receiveReply for " << TopicNameHelper::replyTopicTypeName();
request.promise.set_value(reply.data());
QTimer::singleShot(0, request.requestWatcher, &Request2::finished);
return;
}
}
void sendErrorInternal(const QMetaMethod &errorSlot, int reason, int uniqueId, int requestId, bool directConnection = false)
{
if (errorSlot.isValid())
{
errorSlot.invoke(m_objToInform, directConnection ? Qt::DirectConnection : Qt::QueuedConnection,
Q_ARG(int, reason),
Q_ARG(int, uniqueId),
Q_ARG(int, requestId));
}
}
template < typename T = TOPICTYPE_REQUEST, typename std::enable_if < HasRequestId<T>::value, int >::type = 0 >
void sendError(const ClassicRequest &request, int reason, bool directConnection = false)
{
sendErrorInternal(request.errorSlot, reason, request.uniqueId, request.sample.data().requestId(), directConnection);
}
template < typename T = TOPICTYPE_REQUEST, typename std::enable_if < !HasRequestId<T>::value, int >::type = 0 >
void sendError(const ClassicRequest &request, int reason, bool directConnection = false)
{
sendErrorInternal(request.errorSlot, reason, request.uniqueId, -1, directConnection);
}
bool checkSlots(ClassicRequest &request, const char *replySlot, const char *errorSlot)
{
const QByteArray baReply = QByteArray::number(QSIGNAL_CODE) +
"signalSignature(" +
TopicNameHelper::replyTopicTypeName() +
",int,bool)";
request.replySlot = DDS::Helper::getMethod(m_logger, m_objToInform, replySlot, baReply.data());
if (!request.replySlot.isValid())
return false;
if (errorSlot)
{
const QByteArray baError = QByteArray::number(QSIGNAL_CODE) + "signalSignature(int,int,int)";
request.errorSlot = DDS::Helper::getMethod(m_logger, m_objToInform, errorSlot, baError.data());
if (!request.errorSlot.isValid())
return false;
}
else
request.errorSlot = QMetaMethod();
return true;
}
QObject *m_objToInform;
int m_uniqueId { 0 };
std::queue<ModernRequest<ReplyTopic> > m_modernRequests;
std::queue<ClassicRequest> m_classicRequests;
connext::RequesterParams m_requesterParams;
bool m_dying { false };
std::string m_partitionType;
int m_partitionId;
std::mutex m_requestMutex;
NGL::INGLLogger &m_logger;
};
}
+330
View File
@@ -0,0 +1,330 @@
#include "generic_ftc_threading.h"
#include <QAtomicInt>
#include <QTimer>
#include <QRegularExpression>
#include <chrono>
#include <future>
#ifdef Q_OS_LINUX
#include <pthread.h>
#endif
Threading::~Threading()
{
if (m_threadLifetime != nullptr)
{
m_threadLifetime->stop();
m_threadLifetime.reset();
}
setCleanupNeeded(false);
{
std::lock_guard<std::mutex> l(m_conditionMutex);
setRun(false);
}
if (!m_used)
{
}
if (m_thread.joinable())
{
m_condition.notify_all();
m_thread.join();
}
}
void Threading::preInit() const
{
m_partitionId = partitionId();
m_partitionType = partitionType();
const QString base(QStringLiteral("F%1%2").arg(m_partitionType.left(1)).arg(m_partitionId));
const int baseLen = base.length();
const int maxLen = 16 - 1 - baseLen;
if (maxLen < 1)
{
Q_ASSERT(false);
m_threadName = functionTopicName();
return;
}
const QString orgName(functionTopicName());
QString name;
if (orgName.length() > maxLen)
{
QRegularExpression regexp(QStringLiteral("[A-Z][a-z]*"));
auto matches = regexp.globalMatch(functionTopicName());
QStringList words;
while (matches.hasNext())
{
const auto word = matches.next().capturedTexts().join(QString());
words += word;
}
if (words.isEmpty())
{
Q_ASSERT(false);
m_threadName = functionTopicName();
return;
}
int wordsLeft = words.count();
if (wordsLeft < 1)
{
Q_ASSERT(false);
m_threadName = functionTopicName();
return;
}
int bremse = 20;
while (wordsLeft > 0)
{
if (bremse < 1)
{
Q_ASSERT(false);
break;
}
bremse--;
const int lenRest = maxLen - name.count();
if (lenRest < 1)
{
Q_ASSERT(false);
m_threadName = functionTopicName();
return;
}
const int maxWordLen = lenRest / wordsLeft;
if (words.isEmpty())
{
Q_ASSERT(false);
break;
}
name += words.takeFirst().leftRef(maxWordLen);
wordsLeft--;
}
const int finalLeft = maxLen - name.count();
if (finalLeft < 0 || (finalLeft > 0 && functionTopicName().length() > maxLen))
{
}
}
else
{
name = orgName;
}
m_threadName = QStringLiteral("%1%2[%3]").arg(base, name).arg(qrand(), 8, 16, QLatin1Char('0'));
}
const QString &Threading::threadName() const
{
if (m_threadName.isEmpty())
{
Q_ASSERT(false);
}
return m_threadName;
}
const char *Threading::posixThreadName() const
{
if (m_posixThreadName.empty())
{
m_posixThreadName = threadName().left(15).toStdString();
}
return m_posixThreadName.c_str();
}
void Threading::createThread()
{
if (!isRun())
{
if (m_thread.joinable())
{
m_condition.notify_all();
m_thread.join();
threadDone();
}
m_alreadyThreaded = 0;
setRun(true);
m_thread = std::thread(&Threading::run, this, threadName());
#ifdef Q_OS_LINUX
pthread_setname_np(m_thread.native_handle(), posixThreadName());
#endif
}
m_createThreadCalledBefore = true;
}
void Threading::runThread()
{
m_used = true;
Q_ASSERT(m_createThreadCalledBefore);
m_createThreadCalledBefore = false;
{
std::lock_guard<std::mutex> l(m_conditionMutex);
setWorkAvailable(true);
}
m_condition.notify_all();
if (!m_threadLifetime)
{
m_threadLifetime.reset(new QTimer);
m_threadLifetime->setSingleShot(true);
m_threadLifetime->setInterval(11*1000);
QObject::connect(m_threadLifetime.get(), &QTimer::timeout, m_threadLifetime.get(),
[this]
{
bool workAvailable = false;
bool run = false;
bool working = false;
{
std::lock_guard<std::mutex> l(m_conditionMutex);
workAvailable = isWorkAvailable();
run = isRun();
working = isWorking();
}
if (workAvailable || working)
{
m_threadLifetime->start();
return;
}
m_threadLifetime->deleteLater();
m_threadLifetime = nullptr;
if (!run)
{
Q_ASSERT(false);
return;
}
{
std::lock_guard<std::mutex> l(m_conditionMutex);
setRun(false);
}
m_condition.notify_all();
});
}
m_threadLifetime->start();
}
void Threading::run(const QString &threadName)
{
Q_ASSERT(m_alreadyThreaded == 0);
m_alreadyThreaded = 1;
const std::string _tn = threadName.toStdString();
const char *tn = _tn.c_str();
while (isRun())
{
try
{
{
std::unique_lock<std::mutex> conditionLock(m_conditionMutex);
m_condition.wait(conditionLock, [this]{ return (isWorkAvailable() || !isRun()); });
}
cancellationPoint();
{
std::lock_guard<std::mutex> l(m_conditionMutex);
setWorking(true);
}
work();
{
std::lock_guard<std::mutex> l(m_conditionMutex);
setWorking(false);
}
cancellationPoint();
}
catch (const CancelationPointReached &ex)
{
break;
}
catch (const std::exception &ex)
{
fprintf(stderr, "[!] Threading::run: exception: %s (%s)\n", ex.what(), tn);
}
catch(...)
{
fprintf(stderr, "[!] Threading::run: unknown exception occured (%s) !!!\n", tn);
}
}
if (isCleanupNeeded())
{
cleanupThread();
}
else
{
}
}
+91
View File
@@ -0,0 +1,91 @@
#pragma once
#include <QObject>
#include <QAtomicInt>
#include <QTimer>
#include <thread>
#include <exception>
#include <condition_variable>
class CancelationPointReached : public std::runtime_error
{
public:
explicit CancelationPointReached()
: std::runtime_error("CancelationPointReached")
{}
};
class Threading
{
public:
Threading() = default;
~Threading();
protected:
void preInit() const;
void createThread();
void runThread();
inline bool isRun() const { return (m_run != 0); }
inline void setRun(bool run) { m_run = (run ? 1 : 0); }
inline void setWorkAvailable(bool workAvailable) { m_workAvailable = (workAvailable ? 1 : 0); }
inline bool isWorkAvailable() const { return (m_workAvailable != 0); }
inline void setWorking(bool working) { m_isWorking = (working ? 1 : 0); }
inline bool isWorking() const { return (m_isWorking != 0); }
const QString &threadName() const;
inline const char *posixThreadName() const;
virtual void work() = 0;
virtual QString functionTopicName() const = 0;
virtual int partitionId() const = 0;
virtual QString partitionType() const = 0;
virtual void cleanupThread() = 0;
virtual void threadDone() = 0;
inline void cancellationPoint() const noexcept(false)
{
if (!isRun())
{
throw CancelationPointReached();
}
}
std::mutex m_conditionMutex;
private:
inline bool isCleanupNeeded() const { return (m_cleanupNeeded != 0); }
inline void setCleanupNeeded(bool cleanupNeeded) { m_cleanupNeeded = (cleanupNeeded ? 1 : 0); }
void run(const QString &threadName);
std::unique_ptr<QTimer> m_threadLifetime;
QAtomicInt m_run { 0 };
QAtomicInt m_cleanupNeeded { 1 };
std::thread m_thread;
std::condition_variable m_condition;
QAtomicInt m_workAvailable { 0 };
QAtomicInt m_isWorking { 0 };
mutable QString m_threadName;
mutable std::string m_posixThreadName;
QAtomicInt m_alreadyThreaded;
mutable int m_partitionId { -1 };
mutable QString m_partitionType;
bool m_used { false };
bool m_createThreadCalledBefore { false };
};