1/****************************************************************************
2**
3** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QT3DCORE_QABSTRACTRESOURCESMANAGER_H
41#define QT3DCORE_QABSTRACTRESOURCESMANAGER_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists for the convenience
48// of other Qt classes. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtCore/QHash>
55#include <QtCore/QMutex>
56#include <QtCore/QReadLocker>
57#include <QtCore/QReadWriteLock>
58#include <QtCore/QtGlobal>
59#include <limits>
60
61#include <Qt3DCore/private/qhandle_p.h>
62#include <Qt3DCore/private/qt3dcore_global_p.h>
63
64// Silence complaints about unreferenced local variables in
65// ArrayAllocatingPolicy::deallocateBuckets() when the compiler
66// inlines the call to the dtor and it is empty. Default warning
67// setting re-enabled at bottom of this file
68#if defined(Q_CC_MSVC)
69#pragma warning(disable : 4189)
70#endif
71
72QT_BEGIN_NAMESPACE
73
74namespace Qt3DCore {
75
76template <class Host>
77struct NonLockingPolicy
78{
79 struct ReadLocker
80 {
81 ReadLocker(const NonLockingPolicy*) {}
82 void unlock() {}
83 void relock() {}
84 };
85
86 struct WriteLocker
87 {
88 WriteLocker(const NonLockingPolicy*) {}
89 void unlock() {}
90 void relock() {}
91 };
92
93 struct Locker
94 {
95 Locker(const NonLockingPolicy*) {}
96 void unlock() {}
97 void relock() {}
98 };
99};
100
101template <class Host>
102class ObjectLevelLockingPolicy
103{
104public :
105 ObjectLevelLockingPolicy()
106 {}
107
108 class ReadLocker
109 {
110 public:
111 ReadLocker(const ObjectLevelLockingPolicy *host)
112 : m_locker(&host->m_readWritelock)
113 { }
114
115 void unlock()
116 {
117 m_locker.unlock();
118 }
119
120 void relock()
121 {
122 m_locker.relock();
123 }
124
125 private:
126 QReadLocker m_locker;
127 };
128
129 class WriteLocker
130 {
131 public:
132 WriteLocker(const ObjectLevelLockingPolicy *host)
133 : m_locker(&host->m_readWritelock)
134 { }
135
136 void unlock()
137 {
138 m_locker.unlock();
139 }
140
141 void relock()
142 {
143 m_locker.relock();
144 }
145
146 private:
147 QWriteLocker m_locker;
148 };
149
150 class Locker
151 {
152 public:
153 Locker(const ObjectLevelLockingPolicy *host)
154 : m_locker(&host->m_lock)
155 { }
156
157 void unlock()
158 {
159 m_locker.unlock();
160 }
161
162 void relock()
163 {
164 m_locker.relock();
165 }
166
167 private:
168 QMutexLocker m_locker;
169 };
170
171private:
172 friend class Locker;
173 friend class ReadLocker;
174 friend class WriteLocker;
175 mutable QReadWriteLock m_readWritelock;
176 mutable QMutex m_lock;
177};
178
179template <typename T>
180struct QResourceInfo
181{
182 static const bool needsCleanup = false;
183};
184
185enum
186{
187 Q_REQUIRES_CLEANUP = 0
188};
189
190#define Q_DECLARE_RESOURCE_INFO(TYPE, FLAGS) \
191 namespace Qt3DCore { \
192 template<> \
193 struct QResourceInfo<TYPE > \
194{ \
195 static const bool needsCleanup = (FLAGS & Q_REQUIRES_CLEANUP) == 0;\
196}; \
197} // namespace Qt3DCore
198
199template<typename T>
200class QHandleData : public QHandle<T>::Data
201{
202public:
203 T data;
204};
205
206template<typename T>
207inline T *QHandle<T>::operator->() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
208template<typename T>
209inline T *QHandle<T>::data() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
210
211
212class Q_3DCORE_PRIVATE_EXPORT AlignedAllocator
213{
214public:
215 static void *allocate(uint size);
216 static void release(void *p);
217};
218
219template <typename T>
220class ArrayAllocatingPolicy
221{
222public:
223 typedef QHandleData<T> HandleData;
224 typedef QHandle<T> Handle;
225 ArrayAllocatingPolicy()
226 {
227 }
228
229 ~ArrayAllocatingPolicy()
230 {
231 m_activeHandles.clear();
232 deallocateBuckets();
233 }
234
235 Handle allocateResource()
236 {
237 if (!freeList)
238 allocateBucket();
239 typename Handle::Data *d = freeList;
240 freeList = freeList->nextFree;
241 d->counter = allocCounter;
242 allocCounter += 2; // ensure this will never clash with a pointer in nextFree by keeping the lowest bit set
243 Handle handle(d);
244 m_activeHandles.push_back(handle);
245 return handle;
246 }
247
248 void releaseResource(const Handle &handle)
249 {
250 m_activeHandles.erase(std::remove(m_activeHandles.begin(), m_activeHandles.end(), handle), m_activeHandles.end());
251 typename Handle::Data *d = handle.data_ptr();
252 d->nextFree = freeList;
253 freeList = d;
254 performCleanup(&static_cast<QHandleData<T> *>(d)->data, std::integral_constant<bool, QResourceInfo<T>::needsCleanup>{});
255 }
256
257 T *data(Handle h)
258 {
259 return h.operator->();
260 }
261
262 void for_each(std::function<void(T*)> f)
263 {
264 Bucket *b = firstBucket;
265 while (b) {
266 for (int idx = 0; idx < Bucket::NumEntries; ++idx) {
267 T *t = &b->data[idx].data;
268 f(t);
269 }
270 b = b->header.next;
271 }
272 }
273
274 int count() const { return m_activeHandles.size(); }
275 const std::vector<Handle> &activeHandles() const { return m_activeHandles; }
276
277private:
278 Q_DISABLE_COPY(ArrayAllocatingPolicy)
279 struct Bucket
280 {
281 struct Header
282 {
283 Bucket *next;
284 } header;
285 enum {
286 Size = 4096,
287 NumEntries = (Size - sizeof(Header)) / sizeof(HandleData)
288 };
289 HandleData data[NumEntries];
290 };
291
292 Bucket *firstBucket = 0;
293 std::vector<Handle> m_activeHandles;
294 typename Handle::Data *freeList = 0;
295 int allocCounter = 1;
296
297 void allocateBucket()
298 {
299 // no free handle, allocate a new
300 // allocate aligned memory
301 Bucket *b = static_cast<Bucket *>(AlignedAllocator::allocate(size: sizeof(Bucket)));
302
303 // placement new
304 new (b) Bucket;
305
306 b->header.next = firstBucket;
307 firstBucket = b;
308 for (int i = 0; i < Bucket::NumEntries - 1; ++i) {
309 b->data[i].nextFree = &b->data[i + 1];
310 }
311 b->data[Bucket::NumEntries - 1].nextFree = nullptr;
312 freeList = &b->data[0];
313 }
314
315 void deallocateBuckets()
316 {
317 Bucket *b = firstBucket;
318 while (b) {
319 Bucket *n = b->header.next;
320 // Call dtor explicitly
321 b->~Bucket();
322 // Release aligned memory
323 AlignedAllocator::release(p: b);
324 b = n;
325 }
326 }
327
328 template<typename Q = T>
329 void performCleanup(Q *r, std::integral_constant<bool, true>)
330 {
331 r->cleanup();
332 }
333
334 template<typename Q = T>
335 void performCleanup(Q *, std::integral_constant<bool, false>)
336 {
337 }
338
339};
340
341#ifndef QT_NO_DEBUG_STREAM
342template <typename ValueType, typename KeyType,
343 template <class> class LockingPolicy
344 >
345class QResourceManager;
346
347template <typename ValueType, typename KeyType,
348 template <class> class LockingPolicy = NonLockingPolicy
349 >
350QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
351#endif
352
353template <typename ValueType, typename KeyType,
354 template <class> class LockingPolicy = NonLockingPolicy
355 >
356class QResourceManager
357 : public ArrayAllocatingPolicy<ValueType>
358 , public LockingPolicy< QResourceManager<ValueType, KeyType, LockingPolicy> >
359{
360public:
361 typedef ArrayAllocatingPolicy<ValueType> Allocator;
362 typedef QHandle<ValueType> Handle;
363
364 QResourceManager() :
365 Allocator()
366 {
367 }
368
369 ~QResourceManager()
370 {}
371
372 Handle acquire()
373 {
374 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
375 return Allocator::allocateResource();
376 }
377
378 ValueType* data(const Handle &handle)
379 {
380 return handle.operator->();
381 }
382
383 void release(const Handle &handle)
384 {
385 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
386 Allocator::releaseResource(handle);
387 }
388
389 bool contains(const KeyType &id) const
390 {
391 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
392 return m_keyToHandleMap.contains(id);
393 }
394
395 Handle getOrAcquireHandle(const KeyType &id)
396 {
397 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
398 Handle handle = m_keyToHandleMap.value(id);
399 if (handle.isNull()) {
400 lock.unlock();
401 typename LockingPolicy<QResourceManager>::WriteLocker writeLock(this);
402 // Test that the handle hasn't been set (in the meantime between the read unlock and the write lock)
403 Handle &handleToSet = m_keyToHandleMap[id];
404 if (handleToSet.isNull()) {
405 handleToSet = Allocator::allocateResource();
406 }
407 return handleToSet;
408 }
409 return handle;
410 }
411
412 Handle lookupHandle(const KeyType &id)
413 {
414 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
415 return m_keyToHandleMap.value(id);
416 }
417
418 ValueType *lookupResource(const KeyType &id)
419 {
420 ValueType* ret = nullptr;
421 {
422 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
423 Handle handle = m_keyToHandleMap.value(id);
424 if (!handle.isNull())
425 ret = Allocator::data(handle);
426 }
427 return ret;
428 }
429
430 ValueType *getOrCreateResource(const KeyType &id)
431 {
432 const Handle handle = getOrAcquireHandle(id);
433 return handle.operator->();
434 }
435
436 void releaseResource(const KeyType &id)
437 {
438 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
439 Handle handle = m_keyToHandleMap.take(id);
440 if (!handle.isNull())
441 Allocator::releaseResource(handle);
442 }
443
444protected:
445 QHash<KeyType, Handle > m_keyToHandleMap;
446
447private:
448 friend QDebug operator<< <>(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
449};
450
451#ifndef QT_NO_DEBUG_STREAM
452template <typename ValueType, typename KeyType,
453 template <class> class LockingPolicy
454 >
455QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager)
456{
457 QDebugStateSaver saver(dbg);
458 dbg << "Contains" << manager.count() << "items" << Qt::endl;
459
460 dbg << "Key to Handle Map:" << Qt::endl;
461 const auto end = manager.m_keyToHandleMap.cend();
462 for (auto it = manager.m_keyToHandleMap.cbegin(); it != end; ++it)
463 dbg << "QNodeId =" << it.key() << "Handle =" << it.value() << Qt::endl;
464
465// dbg << "Resources:" << Qt::endl;
466// dbg << manager.m_handleManager;
467 return dbg;
468}
469#endif
470
471}// Qt3D
472
473QT_END_NAMESPACE
474
475#if defined(Q_CC_MSVC)
476#pragma warning(default : 4189)
477#endif
478
479#endif // QT3DCORE_QABSTRACTRESOURCESMANAGER_H
480

source code of qt3d/src/core/resources/qresourcemanager_p.h