1 /*
2  *  Copyright (c) 2020 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 #include "KisBusyWaitBroker.h"
19 
20 #include <QGlobalStatic>
21 #include <QMutex>
22 #include <QMutexLocker>
23 
24 #include <QThread>
25 #include <QApplication>
26 
27 #include "kis_image.h"
28 
29 
30 Q_GLOBAL_STATIC(KisBusyWaitBroker, s_instance)
31 
32 
33 struct KisBusyWaitBroker::Private
34 {
35     QMutex lock;
36     QHash<KisImage*, int> waitingOnImages;
37     int guiThreadLockCount = 0;
38 
39     std::function<void(KisImageSP)> feedbackCallback;
40 };
41 
42 
KisBusyWaitBroker()43 KisBusyWaitBroker::KisBusyWaitBroker()
44     : m_d(new Private)
45 {
46 }
47 
~KisBusyWaitBroker()48 KisBusyWaitBroker::~KisBusyWaitBroker()
49 {
50 }
51 
instance()52 KisBusyWaitBroker *KisBusyWaitBroker::instance()
53 {
54     return s_instance;
55 }
56 
notifyWaitOnImageStarted(KisImage * image)57 void KisBusyWaitBroker::notifyWaitOnImageStarted(KisImage* image)
58 {
59     if (QThread::currentThread() != qApp->thread()) return;
60 
61     bool needsStartCallback = false;
62 
63     {
64         QMutexLocker l(&m_d->lock);
65 
66         m_d->guiThreadLockCount++;
67         m_d->waitingOnImages[image]++;
68 
69         needsStartCallback = m_d->waitingOnImages[image] == 1;
70     }
71 
72     if (m_d->feedbackCallback && needsStartCallback && image->refCount()) {
73         m_d->feedbackCallback(image);
74     }
75 }
76 
notifyWaitOnImageEnded(KisImage * image)77 void KisBusyWaitBroker::notifyWaitOnImageEnded(KisImage* image)
78 {
79     if (QThread::currentThread() != qApp->thread()) return;
80 
81     {
82         QMutexLocker l(&m_d->lock);
83         m_d->guiThreadLockCount--;
84 
85         m_d->waitingOnImages[image]--;
86         KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->waitingOnImages[image] >= 0);
87 
88         if (m_d->waitingOnImages[image] == 0) {
89             m_d->waitingOnImages.remove(image);
90         }
91     }
92 }
93 
notifyGeneralWaitStarted()94 void KisBusyWaitBroker::notifyGeneralWaitStarted()
95 {
96     if (QThread::currentThread() != qApp->thread()) return;
97 
98     QMutexLocker l(&m_d->lock);
99     m_d->guiThreadLockCount++;
100 }
101 
notifyGeneralWaitEnded()102 void KisBusyWaitBroker::notifyGeneralWaitEnded()
103 {
104     if (QThread::currentThread() != qApp->thread()) return;
105 
106     QMutexLocker l(&m_d->lock);
107     m_d->guiThreadLockCount--;
108 }
109 
setFeedbackCallback(std::function<void (KisImageSP)> callback)110 void KisBusyWaitBroker::setFeedbackCallback(std::function<void (KisImageSP)> callback)
111 {
112     m_d->feedbackCallback = callback;
113 }
114 
guiThreadIsWaitingForBetterWeather() const115 bool KisBusyWaitBroker::guiThreadIsWaitingForBetterWeather() const
116 {
117     return m_d->guiThreadLockCount;
118 }
119 
120 
121