File indexing completed on 2024-05-12 15:58:52

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #ifndef KISSAFEBLOCKINGQUEUECONNECTIONPROXY_H
0007 #define KISSAFEBLOCKINGQUEUECONNECTIONPROXY_H
0008 
0009 #include <QObject>
0010 #include <QQueue>
0011 #include <functional>
0012 #include "kis_signal_compressor_with_param.h"
0013 #include "kis_assert.h"
0014 #include "kritaimage_export.h"
0015 
0016 namespace KisSafeBlockingQueueConnectionProxyPrivate {
0017 void KRITAIMAGE_EXPORT passBlockingSignalSafely(FunctionToSignalProxy &source, SignalToFunctionProxy &destination);
0018 void KRITAIMAGE_EXPORT initProxyObject(QObject *object);
0019 }
0020 
0021 
0022 /**
0023  * A special class for safe forwarding of blocking-queued signal to the GUI thread.
0024  *
0025  * The class automatically resolves deadlocks when GUI thread blocks on the image.
0026  * This tie-breaking algorithm is implemented via KisBusyWaitBroker.
0027  *
0028  * Usage:
0029  *
0030  *        \code{.cpp}
0031  *
0032  *        // create the proxy
0033  *        KisSafeBlockingQueueConnectionProxy<QTransform> proxy(
0034  *            std::bind(&KisShapeLayer::slotTransformShapes, shapeLayer));
0035  *
0036  *        // emit synchronous signal with deadlock-avoidance
0037  *        proxy.start(QTransform::fromScale(0.5, 0.5));
0038  *
0039  *        \endcode
0040  */
0041 template <typename T>
0042 class KisSafeBlockingQueueConnectionProxy
0043 {
0044     using CallbackFunction = std::function<void (T)>;
0045 public:
0046     KisSafeBlockingQueueConnectionProxy(CallbackFunction function)
0047         : m_function(function),
0048           m_destination(std::bind(&KisSafeBlockingQueueConnectionProxy::fakeSlotTimeout, this))
0049     {
0050         KisSafeBlockingQueueConnectionProxyPrivate::initProxyObject(&m_source);
0051         KisSafeBlockingQueueConnectionProxyPrivate::initProxyObject(&m_destination);
0052 
0053         QObject::connect(&m_source, SIGNAL(timeout()), &m_destination, SLOT(start()), Qt::BlockingQueuedConnection);
0054     }
0055 
0056     void start(T value) {
0057         const int sanityQueueSize = m_value.size();
0058 
0059         m_value.enqueue(value);
0060         KisSafeBlockingQueueConnectionProxyPrivate::passBlockingSignalSafely(m_source, m_destination);
0061 
0062         KIS_SAFE_ASSERT_RECOVER_NOOP(m_value.size() == sanityQueueSize);
0063     }
0064 
0065 private:
0066     void fakeSlotTimeout() {
0067         KIS_SAFE_ASSERT_RECOVER_RETURN(!m_value.isEmpty());
0068         m_function(m_value.dequeue());
0069     }
0070 
0071 private:
0072     CallbackFunction m_function;
0073     FunctionToSignalProxy m_source;
0074     SignalToFunctionProxy m_destination;
0075     QQueue<T> m_value;
0076 };
0077 
0078 /**
0079  * An override of KisSafeBlockingQueueConnectionProxy for forwarding signals
0080  * without any parameters.
0081  */
0082 template <>
0083 class KisSafeBlockingQueueConnectionProxy<void>
0084 {
0085     using CallbackFunction = std::function<void ()>;
0086 public:
0087     KisSafeBlockingQueueConnectionProxy(CallbackFunction function)
0088         : m_function(function),
0089           m_destination(std::bind(&KisSafeBlockingQueueConnectionProxy::fakeSlotTimeout, this))
0090     {
0091         KisSafeBlockingQueueConnectionProxyPrivate::initProxyObject(&m_source);
0092         KisSafeBlockingQueueConnectionProxyPrivate::initProxyObject(&m_destination);
0093 
0094         QObject::connect(&m_source, SIGNAL(timeout()), &m_destination, SLOT(start()), Qt::BlockingQueuedConnection);
0095     }
0096 
0097     void start() {
0098         KisSafeBlockingQueueConnectionProxyPrivate::passBlockingSignalSafely(m_source, m_destination);
0099     }
0100 
0101 private:
0102     void fakeSlotTimeout() {
0103         m_function();
0104     }
0105 
0106 private:
0107     CallbackFunction m_function;
0108     FunctionToSignalProxy m_source;
0109     SignalToFunctionProxy m_destination;
0110 };
0111 
0112 #endif // KISSAFEBLOCKINGQUEUECONNECTIONPROXY_H