Renamed Component to QComponent
[qt:qt3d.git] / src / core / qchangearbiter.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt3D module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qchangearbiter.h"
43 #include "qcomponent.h"
44 #include "qjobmanagerinterface.h"
45
46 #include "corelogging.h"
47 #include <QMutexLocker>
48 #include <QReadLocker>
49 #include <QThread>
50 #include <QWriteLocker>
51
52 QT_BEGIN_NAMESPACE
53
54 namespace Qt3D {
55
56 QChangeArbiter::QChangeArbiter(QObject *parent)
57     : QObject(parent)
58     , m_mutex(QMutex::Recursive)
59     , m_jobManager(0)
60 {
61     // The QMutex has to be recursive to handle the case where :
62     // 1) SyncChanges is called, mutex is locked
63     // 2) Changes are distributed
64     // 3) An observer decides to register a new observable upon receiving notification
65     // 4) registerObserver locks the mutex once again -> we need recursion otherwise deadlock
66     // 5) Mutex is unlocked - leaving registerObserver
67     // 6) Mutex is unlocked - leaving SyncChanges
68 }
69
70 void QChangeArbiter::initialize(QJobManagerInterface *jobManager)
71 {
72     Q_CHECK_PTR(jobManager);
73     m_jobManager = jobManager;
74
75     // Init TLS for the change queues
76     m_jobManager->waitForPerThreadFunction(QChangeArbiter::createThreadLocalChangeQueue, this);
77 }
78
79 void QChangeArbiter::distributeQueueChanges(ChangeQueue *changeQueue)
80 {
81     Q_FOREACH (const QSceneChangePtr &change, *changeQueue) {
82         // Lookup which observers care about the subject this change came from
83         // and distribute the change to them
84         if (change.isNull())
85             continue;
86         switch (change->m_subjectType) {
87         case QSceneChange::ObservableType: {
88             QObservableInterface *subject = change->m_subject.m_observable;
89             if (m_aspectObservations.contains(subject)) {
90                 QObserverList &observers = m_aspectObservations[subject];
91                 Q_FOREACH (const QObserverPair &observer, observers) {
92                     if ((change->m_type & observer.first))
93                         observer.second->sceneChangeEvent(change);
94                 }
95             }
96             break;
97         }
98
99         case QSceneChange::NodeType: {
100             QNode *subject = change->m_subject.m_node;
101             if (m_nodeObservations.contains(subject)) {
102                 QObserverList &observers = m_nodeObservations[subject];
103                 Q_FOREACH (const QObserverPair&observer, observers) {
104                     if ((change->m_type & observer.first))
105                         observer.second->sceneChangeEvent(change);
106                 }
107             }
108             break;
109         }
110         }
111     }
112 }
113
114 void QChangeArbiter::syncChanges()
115 {
116     QMutexLocker locker(&m_mutex);
117     Q_FOREACH (QChangeArbiter::ChangeQueue *changeQueue, m_changeQueues) {
118         distributeQueueChanges(changeQueue);
119         changeQueue->clear();
120     }
121
122     Q_FOREACH (ChangeQueue *changeQueue, m_lockingChangeQueues) {
123         distributeQueueChanges(changeQueue);
124         changeQueue->clear();
125     }
126 }
127
128 void QChangeArbiter::registerObserver(QObserverInterface *observer,
129                                       QObservableInterface *observable,
130                                       ChangeFlags changeFlags)
131 {
132     QMutexLocker locker(&m_mutex);
133     QObserverList &observerList = m_aspectObservations[observable];
134     registerObserverHelper(observerList, observer, observable, changeFlags);
135 }
136
137 void QChangeArbiter::registerObserver(QObserverInterface *observer,
138                                       QNode *observable,
139                                       ChangeFlags changeFlags)
140 {
141     QMutexLocker locker(&m_mutex);
142     QObserverList &observerList = m_nodeObservations[observable];
143     registerObserverHelper(observerList, observer, observable, changeFlags);
144 }
145
146 void QChangeArbiter::registerObserverHelper(QObserverList &observerList,
147                                             QObserverInterface *observer,
148                                             QObservableInterface *observable,
149                                             ChangeFlags changeFlags)
150 {
151     Q_ASSERT(observer);
152     Q_ASSERT(observable);
153     qCDebug(ChangeArbiter) << Q_FUNC_INFO;
154
155     // Store info about which observers are watching which observables.
156     // Protect access as this could be called from any thread
157     observerList.append(QObserverPair(changeFlags, observer));
158
159     // Register ourselves with the observable as the intermediary
160     observable->registerObserver(this);
161 }
162
163 void QChangeArbiter::unregisterObserver(QObserverInterface *observer,
164                                         QObservableInterface *subject)
165 {
166     QMutexLocker locker(&m_mutex);
167     if (m_aspectObservations.contains(subject)) {
168         QObserverList &observers = m_aspectObservations[subject];
169         for (int i = observers.count() - 1; i >= 0; i--) {
170             if (observers[i].second == observer)
171                 observers.removeAt(i);
172         }
173     }
174 }
175
176 void QChangeArbiter::unregisterObserver(QObserverInterface *observer, QNode *subject)
177 {
178     QMutexLocker locker(&m_mutex);
179     if (m_nodeObservations.contains(subject)) {
180         QObserverList &observers = m_nodeObservations[subject];
181         for (int i = observers.count() - 1; i >= 0; i--) {
182             if (observers[i].second == observer)
183                 observers.removeAt(i);
184         }
185     }
186 }
187
188 void QChangeArbiter::sceneChangeEvent(const QSceneChangePtr &e)
189 {
190     //    qCDebug(ChangeArbiter) << Q_FUNC_INFO << QThread::currentThread();
191
192     // Add the change to the thread local storage queue - no locking required => yay!
193     ChangeQueue *localChangeQueue = m_tlsChangeQueue.localData();
194     localChangeQueue->append(e);
195
196     //    qCDebug(ChangeArbiter) << "Change queue for thread" << QThread::currentThread() << "now contains" << localChangeQueue->count() << "items";
197 }
198
199 void QChangeArbiter::sceneChangeEventWithLock(const QSceneChangePtr &e)
200 {
201     QMutexLocker locker(&m_mutex);
202     sceneChangeEvent(e);
203 }
204
205 void QChangeArbiter::createUnmanagedThreadLocalChangeQueue(void *changeArbiter)
206 {
207     Q_CHECK_PTR(changeArbiter);
208
209     QChangeArbiter *arbiter = static_cast<QChangeArbiter *>(changeArbiter);
210
211     qCDebug(ChangeArbiter) << Q_FUNC_INFO << QThread::currentThread();
212     if (!arbiter->m_tlsChangeQueue.hasLocalData()) {
213         ChangeQueue *localChangeQueue = new ChangeQueue;
214         arbiter->m_tlsChangeQueue.setLocalData(localChangeQueue);
215
216         QMutexLocker locker(&(arbiter->m_mutex));
217         arbiter->m_lockingChangeQueues.append(localChangeQueue);
218     }
219 }
220
221 static void destroyUnmanagedThreadLocalChangeQueue(void *changeArbiter)
222 {
223     // TODO: Implement me!
224     Q_UNUSED(changeArbiter);
225 }
226
227 void QChangeArbiter::createThreadLocalChangeQueue(void *changeArbiter)
228 {
229     Q_CHECK_PTR(changeArbiter);
230
231     QChangeArbiter *arbiter = static_cast<QChangeArbiter *>(changeArbiter);
232
233     qCDebug(ChangeArbiter) << Q_FUNC_INFO << QThread::currentThread();
234     if (!arbiter->m_tlsChangeQueue.hasLocalData()) {
235         ChangeQueue *localChangeQueue = new ChangeQueue;
236         arbiter->m_tlsChangeQueue.setLocalData(localChangeQueue);
237
238         QMutexLocker locker(&(arbiter->m_mutex));
239         arbiter->m_changeQueues.append(localChangeQueue);
240     }
241 }
242
243 static void destroyThreadLocalChangeQueue(void *changeArbiter)
244 {
245     // TODO: Implement me!
246     Q_UNUSED(changeArbiter);
247 }
248
249 } // namespace Qt3D
250
251 QT_END_NAMESPACE