blob: b3837fb1f3eb18c76ea37a77ae8499bac104288d [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QT3DCORE_QABSTRACTRESOURCESMANAGER_H
#define QT3DCORE_QABSTRACTRESOURCESMANAGER_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of other Qt classes. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/QHash>
#include <QtCore/QMutex>
#include <QtCore/QReadLocker>
#include <QtCore/QReadWriteLock>
#include <QtCore/QtGlobal>
#include <limits>
#include <Qt3DCore/private/qhandle_p.h>
#include <Qt3DCore/private/qt3dcore_global_p.h>
// Silence complaints about unreferenced local variables in
// ArrayAllocatingPolicy::deallocateBuckets() when the compiler
// inlines the call to the dtor and it is empty. Default warning
// setting re-enabled at bottom of this file
#if defined(Q_CC_MSVC)
#pragma warning(disable : 4189)
#endif
QT_BEGIN_NAMESPACE
namespace Qt3DCore {
template <class Host>
struct NonLockingPolicy
{
struct ReadLocker
{
ReadLocker(const NonLockingPolicy*) {}
void unlock() {}
void relock() {}
};
struct WriteLocker
{
WriteLocker(const NonLockingPolicy*) {}
void unlock() {}
void relock() {}
};
struct Locker
{
Locker(const NonLockingPolicy*) {}
void unlock() {}
void relock() {}
};
};
template <class Host>
class ObjectLevelLockingPolicy
{
public :
ObjectLevelLockingPolicy()
{}
class ReadLocker
{
public:
ReadLocker(const ObjectLevelLockingPolicy *host)
: m_locker(&host->m_readWritelock)
{ }
void unlock()
{
m_locker.unlock();
}
void relock()
{
m_locker.relock();
}
private:
QReadLocker m_locker;
};
class WriteLocker
{
public:
WriteLocker(const ObjectLevelLockingPolicy *host)
: m_locker(&host->m_readWritelock)
{ }
void unlock()
{
m_locker.unlock();
}
void relock()
{
m_locker.relock();
}
private:
QWriteLocker m_locker;
};
class Locker
{
public:
Locker(const ObjectLevelLockingPolicy *host)
: m_locker(&host->m_lock)
{ }
void unlock()
{
m_locker.unlock();
}
void relock()
{
m_locker.relock();
}
private:
QMutexLocker m_locker;
};
private:
friend class Locker;
friend class ReadLocker;
friend class WriteLocker;
mutable QReadWriteLock m_readWritelock;
mutable QMutex m_lock;
};
template <typename T>
struct QResourceInfo
{
enum
{
needsCleanup = false
};
};
template <>
struct QResourceInfo<void>
{
enum
{
needsCleanup = false
};
};
enum
{
Q_REQUIRES_CLEANUP = 0
};
#define Q_DECLARE_RESOURCE_INFO(TYPE, FLAGS) \
namespace Qt3DCore { \
template<> \
struct QResourceInfo<TYPE > \
{ \
enum \
{ \
needsCleanup = ((FLAGS & Q_REQUIRES_CLEANUP) == 0) \
}; \
}; \
} // namespace Qt3DCore
template <int v>
struct Int2Type
{
enum
{
value = v
};
};
template<typename T>
class QHandleData : public QHandle<T>::Data
{
public:
T data;
};
template<typename T>
inline T *QHandle<T>::operator->() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
template<typename T>
inline T *QHandle<T>::data() const { return (d && counter == d->counter) ? &static_cast<QHandleData<T> *>(d)->data : nullptr; }
class Q_3DCORE_PRIVATE_EXPORT AlignedAllocator
{
public:
static void *allocate(uint size);
static void release(void *p);
};
template <typename T>
class ArrayAllocatingPolicy
{
public:
typedef QHandleData<T> HandleData;
typedef QHandle<T> Handle;
ArrayAllocatingPolicy()
{
}
~ArrayAllocatingPolicy()
{
m_activeHandles.clear();
deallocateBuckets();
}
Handle allocateResource()
{
if (!freeList)
allocateBucket();
typename Handle::Data *d = freeList;
freeList = freeList->nextFree;
d->counter = allocCounter;
allocCounter += 2; // ensure this will never clash with a pointer in nextFree by keeping the lowest bit set
Handle handle(d);
m_activeHandles.push_back(handle);
return handle;
}
void releaseResource(const Handle &handle)
{
m_activeHandles.removeOne(handle);
typename Handle::Data *d = handle.data_ptr();
d->nextFree = freeList;
freeList = d;
performCleanup(&static_cast<QHandleData<T> *>(d)->data, Int2Type<QResourceInfo<T>::needsCleanup>());
}
T *data(Handle h)
{
return h.operator->();
}
void for_each(std::function<void(T*)> f)
{
Bucket *b = firstBucket;
while (b) {
for (int idx = 0; idx < Bucket::NumEntries; ++idx) {
T *t = &b->data[idx].data;
f(t);
}
b = b->header.next;
}
}
int count() const { return m_activeHandles.size(); }
QVector<Handle> activeHandles() const { return m_activeHandles; }
private:
Q_DISABLE_COPY(ArrayAllocatingPolicy)
struct Bucket
{
struct Header
{
Bucket *next;
} header;
enum {
Size = 4096,
NumEntries = (Size - sizeof(Header)) / sizeof(HandleData)
};
HandleData data[NumEntries];
};
Bucket *firstBucket = 0;
QVector<Handle > m_activeHandles;
typename Handle::Data *freeList = 0;
int allocCounter = 1;
void allocateBucket()
{
// no free handle, allocate a new
// allocate aligned memory
Bucket *b = static_cast<Bucket *>(AlignedAllocator::allocate(sizeof(Bucket)));
// placement new
new (b) Bucket;
b->header.next = firstBucket;
firstBucket = b;
for (int i = 0; i < Bucket::NumEntries - 1; ++i) {
b->data[i].nextFree = &b->data[i + 1];
}
b->data[Bucket::NumEntries - 1].nextFree = nullptr;
freeList = &b->data[0];
}
void deallocateBuckets()
{
Bucket *b = firstBucket;
while (b) {
Bucket *n = b->header.next;
// Call dtor explicitly
b->~Bucket();
// Release aligned memory
AlignedAllocator::release(b);
b = n;
}
}
void performCleanup(T *r, Int2Type<true>)
{
r->cleanup();
}
void performCleanup(T *, Int2Type<false>)
{}
};
#ifndef QT_NO_DEBUG_STREAM
template <typename ValueType, typename KeyType,
template <class> class LockingPolicy
>
class QResourceManager;
template <typename ValueType, typename KeyType,
template <class> class LockingPolicy = NonLockingPolicy
>
QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
#endif
template <typename ValueType, typename KeyType,
template <class> class LockingPolicy = NonLockingPolicy
>
class QResourceManager
: public ArrayAllocatingPolicy<ValueType>
, public LockingPolicy< QResourceManager<ValueType, KeyType, LockingPolicy> >
{
public:
typedef ArrayAllocatingPolicy<ValueType> Allocator;
typedef QHandle<ValueType> Handle;
QResourceManager() :
Allocator()
{
}
~QResourceManager()
{}
Handle acquire()
{
typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
return Allocator::allocateResource();
}
ValueType* data(const Handle &handle)
{
return handle.operator->();
}
void release(const Handle &handle)
{
typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
Allocator::releaseResource(handle);
}
bool contains(const KeyType &id) const
{
typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
return m_keyToHandleMap.contains(id);
}
Handle getOrAcquireHandle(const KeyType &id)
{
typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
Handle handle = m_keyToHandleMap.value(id);
if (handle.isNull()) {
lock.unlock();
typename LockingPolicy<QResourceManager>::WriteLocker writeLock(this);
// Test that the handle hasn't been set (in the meantime between the read unlock and the write lock)
Handle &handleToSet = m_keyToHandleMap[id];
if (handleToSet.isNull()) {
handleToSet = Allocator::allocateResource();
}
return handleToSet;
}
return handle;
}
Handle lookupHandle(const KeyType &id)
{
typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
return m_keyToHandleMap.value(id);
}
ValueType *lookupResource(const KeyType &id)
{
ValueType* ret = nullptr;
{
typename LockingPolicy<QResourceManager>::ReadLocker lock(this);
Handle handle = m_keyToHandleMap.value(id);
if (!handle.isNull())
ret = Allocator::data(handle);
}
return ret;
}
ValueType *getOrCreateResource(const KeyType &id)
{
const Handle handle = getOrAcquireHandle(id);
return handle.operator->();
}
void releaseResource(const KeyType &id)
{
typename LockingPolicy<QResourceManager>::WriteLocker lock(this);
Handle handle = m_keyToHandleMap.take(id);
if (!handle.isNull())
Allocator::releaseResource(handle);
}
protected:
QHash<KeyType, Handle > m_keyToHandleMap;
private:
friend QDebug operator<< <>(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager);
};
#ifndef QT_NO_DEBUG_STREAM
template <typename ValueType, typename KeyType,
template <class> class LockingPolicy
>
QDebug operator<<(QDebug dbg, const QResourceManager<ValueType, KeyType, LockingPolicy> &manager)
{
QDebugStateSaver saver(dbg);
dbg << "Contains" << manager.count() << "items" << endl;
dbg << "Key to Handle Map:" << endl;
const auto end = manager.m_keyToHandleMap.cend();
for (auto it = manager.m_keyToHandleMap.cbegin(); it != end; ++it)
dbg << "QNodeId =" << it.key() << "Handle =" << it.value() << endl;
// dbg << "Resources:" << endl;
// dbg << manager.m_handleManager;
return dbg;
}
#endif
}// Qt3D
QT_END_NAMESPACE
#if defined(Q_CC_MSVC)
#pragma warning(default : 4189)
#endif
#endif // QT3DCORE_QABSTRACTRESOURCESMANAGER_H