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 enum
183 {
184 needsCleanup = false
185 };
186};
187
188template <>
189struct QResourceInfo<void>
190{
191 enum
192 {
193 needsCleanup = false
194 };
195};
196
197enum
198{
199 Q_REQUIRES_CLEANUP = 0
200};
201
202#define Q_DECLARE_RESOURCE_INFO(TYPE, FLAGS) \
203 namespace Qt3DCore { \
204 template<> \
205 struct QResourceInfo<TYPE > \
206{ \
207 enum \
208{ \
209 needsCleanup = ((FLAGS & Q_REQUIRES_CLEANUP) == 0) \
210}; \
211}; \
212} // namespace Qt3DCore
213
214template <int v>
215struct Int2Type
216{
217 enum
218 {
219 value = v
220 };
221};
222
223template<typename T>
224class QHandleData : public QHandle<T>::Data
225{
226public:
227 T data;
228};
229
230template<typename T>
231inline T *QHandle<T>::operator->() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
232template<typename T>
233inline T *QHandle<T>::data() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
234
235
236class Q_3DCORE_PRIVATE_EXPORT AlignedAllocator
237{
238public:
239 static void *allocate(uint size);
240 static void release(void *p);
241};
242
243template <typename T>
244class ArrayAllocatingPolicy
245{
246public:
247 typedef QHandleData<T> HandleData;
248 typedef QHandle<T> Handle;
249 ArrayAllocatingPolicy()
250 {
251 }
252
253 ~ArrayAllocatingPolicy()
254 {
255 m_activeHandles.clear();
256 deallocateBuckets();
257 }
258
259 Handle allocateResource()
260 {
261 if (!freeList)
262 allocateBucket();
263 typename Handle::Data *d = freeList;
264 freeList = freeList->nextFree;
265 d->counter = allocCounter;
266 allocCounter += 2; // ensure this will never clash with a pointer in nextFree by keeping the lowest bit set
267 Handle handle(d);
268 m_activeHandles.push_back(handle);
269 return handle;
270 }
271
272 void releaseResource(const Handle &handle)
273 {
274 m_activeHandles.removeOne(handle);
275 typename Handle::Data *d = handle.data_ptr();
276 d->nextFree = freeList;
277 freeList = d;
278 performCleanup(&static_cast<QHandleData<T> *>(d)->data, Int2Type<QResourceInfo<T>::needsCleanup>());
279 }
280
281 T *data(Handle h)
282 {
283 return h.operator->();
284 }
285
286 void for_each(std::function<void(T*)> f)
287 {
288 Bucket *b = firstBucket;
289 while (b) {
290 for (int idx = 0; idx < Bucket::NumEntries; ++idx) {
291 T *t = &b->data[idx].data;
292 f(t);
293 }
294 b = b->header.next;
295 }
296 }
297
298 int count() const { return m_activeHandles.size(); }
299 QVector<Handle> activeHandles() const { return m_activeHandles; }
300
301private:
302 Q_DISABLE_COPY(ArrayAllocatingPolicy)
303 struct Bucket
304 {
305 struct Header
306 {
307 Bucket *next;
308 } header;
309 enum {
310 Size = 4096,
311 NumEntries = (Size - sizeof(Header)) / sizeof(HandleData)
312 };
313 HandleData data[NumEntries];
314 };
315
316 Bucket *firstBucket = 0;
317 QVector<Handle > m_activeHandles;
318 typename Handle::Data *freeList = 0;
319 int allocCounter = 1;
320
321 void allocateBucket()
322 {
323 // no free handle, allocate a new
324 // allocate aligned memory
325 Bucket *b = static_cast<Bucket *>(AlignedAllocator::allocate(sizeof(Bucket)));
326
327 // placement new
328 new (b) Bucket;
329
330 b->header.next = firstBucket;
331 firstBucket = b;
332 for (int i = 0; i < Bucket::NumEntries - 1; ++i) {
333 b->data[i].nextFree = &b->data[i + 1];
334 }
335 b->data[Bucket::NumEntries - 1].nextFree = nullptr;
336 freeList = &b->data[0];
337 }
338
339 void deallocateBuckets()
340 {
341 Bucket *b = firstBucket;
342 while (b) {
343 Bucket *n = b->header.next;
344 // Call dtor explicitly
345 b->~Bucket();
346 // Release aligned memory
347 AlignedAllocator::release(b);
348 b = n;
349 }
350 }
351
352 void performCleanup(T *r, Int2Type<true>)
353 {
354 r->cleanup();
355 }
356
357 void performCleanup(T *, Int2Type<false>)
358 {}
359
360};
361
362#ifndef QT_NO_DEBUG_STREAM
363template <typename ValueType, typename KeyType,
364 template <class> class LockingPolicy
365 >
366class QResourceManager;
367
368template <typename ValueType, typename KeyType,
369 template <class> class LockingPolicy = NonLockingPolicy
370 >
371QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
372#endif
373
374template <typename ValueType, typename KeyType,
375 template <class> class LockingPolicy = NonLockingPolicy
376 >
377class QResourceManager
378 : public ArrayAllocatingPolicy<ValueType>
379 , public LockingPolicy< QResourceManager<ValueType, KeyType, LockingPolicy> >
380{
381public:
382 typedef ArrayAllocatingPolicy<ValueType> Allocator;
383 typedef QHandle<ValueType> Handle;
384
385 QResourceManager() :
386 Allocator()
387 {
388 }
389
390 ~QResourceManager()
391 {}
392
393 Handle acquire()
394 {
395 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
396 return Allocator::allocateResource();
397 }
398
399 ValueType* data(const Handle &handle)
400 {
401 return handle.operator->();
402 }
403
404 void release(const Handle &handle)
405 {
406 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
407 Allocator::releaseResource(handle);
408 }
409
410 bool contains(const KeyType &id) const
411 {
412 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
413 return m_keyToHandleMap.contains(id);
414 }
415
416 Handle getOrAcquireHandle(const KeyType &id)
417 {
418 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
419 Handle handle = m_keyToHandleMap.value(id);
420 if (handle.isNull()) {
421 lock.unlock();
422 typename LockingPolicy<QResourceManager>::WriteLocker writeLock(this);
423 // Test that the handle hasn't been set (in the meantime between the read unlock and the write lock)
424 Handle &handleToSet = m_keyToHandleMap[id];
425 if (handleToSet.isNull()) {
426 handleToSet = Allocator::allocateResource();
427 }
428 return handleToSet;
429 }
430 return handle;
431 }
432
433 Handle lookupHandle(const KeyType &id)
434 {
435 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
436 return m_keyToHandleMap.value(id);
437 }
438
439 ValueType *lookupResource(const KeyType &id)
440 {
441 ValueType* ret = nullptr;
442 {
443 typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
444 Handle handle = m_keyToHandleMap.value(id);
445 if (!handle.isNull())
446 ret = Allocator::data(handle);
447 }
448 return ret;
449 }
450
451 ValueType *getOrCreateResource(const KeyType &id)
452 {
453 const Handle handle = getOrAcquireHandle(id);
454 return handle.operator->();
455 }
456
457 void releaseResource(const KeyType &id)
458 {
459 typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
460 Handle handle = m_keyToHandleMap.take(id);
461 if (!handle.isNull())
462 Allocator::releaseResource(handle);
463 }
464
465protected:
466 QHash<KeyType, Handle > m_keyToHandleMap;
467
468private:
469 friend QDebug operator<< <>(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
470};
471
472#ifndef QT_NO_DEBUG_STREAM
473template <typename ValueType, typename KeyType,
474 template <class> class LockingPolicy
475 >
476QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager)
477{
478 QDebugStateSaver saver(dbg);
479 dbg << "Contains" << manager.count() << "items" << endl;
480
481 dbg << "Key to Handle Map:" << endl;
482 const auto end = manager.m_keyToHandleMap.cend();
483 for (auto it = manager.m_keyToHandleMap.cbegin(); it != end; ++it)
484 dbg << "QNodeId =" << it.key() << "Handle =" << it.value() << endl;
485
486// dbg << "Resources:" << endl;
487// dbg << manager.m_handleManager;
488 return dbg;
489}
490#endif
491
492}// Qt3D
493
494QT_END_NAMESPACE
495
496#if defined(Q_CC_MSVC)
497#pragma warning(default : 4189)
498#endif
499
500#endif // QT3DCORE_QABSTRACTRESOURCESMANAGER_H
501