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