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:GPL-EXCEPT$ |
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 General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | #include <QList> |
31 | #include <Qt3DCore/private/qhandle_p.h> |
32 | #include <Qt3DCore/private/qresourcemanager_p.h> |
33 | |
34 | class tst_QResourceManager : public QObject |
35 | { |
36 | Q_OBJECT |
37 | public: |
38 | tst_QResourceManager() {} |
39 | ~tst_QResourceManager() {} |
40 | |
41 | private slots: |
42 | void createResourcesManager(); |
43 | void acquireResources(); |
44 | void getResources(); |
45 | void registerResourcesResize(); |
46 | void removeResource(); |
47 | void lookupResource(); |
48 | void releaseResource(); |
49 | void heavyDutyMultiThreadedAccess(); |
50 | void heavyDutyMultiThreadedAccessRelease(); |
51 | void collectResources(); |
52 | void activeHandles(); |
53 | void checkCleanup(); |
54 | }; |
55 | |
56 | class tst_ArrayResource |
57 | { |
58 | public: |
59 | tst_ArrayResource() : m_value(0) |
60 | {} |
61 | |
62 | void cleanup() { m_value = 0; } |
63 | |
64 | QAtomicInt m_value; |
65 | }; |
66 | |
67 | QT_BEGIN_NAMESPACE |
68 | Q_DECLARE_RESOURCE_INFO(tst_ArrayResource, Q_REQUIRES_CLEANUP) |
69 | QT_END_NAMESPACE |
70 | |
71 | typedef Qt3DCore::QHandle<tst_ArrayResource> tHandle; |
72 | |
73 | void tst_QResourceManager::createResourcesManager() |
74 | { |
75 | Qt3DCore::QResourceManager<tst_ArrayResource, int> manager; |
76 | } |
77 | |
78 | /*! |
79 | * Check that the handles returned when a registering resources |
80 | * have a correct index and counter. |
81 | */ |
82 | void tst_QResourceManager::acquireResources() |
83 | { |
84 | Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager; |
85 | |
86 | QList<tHandle> handles; |
87 | |
88 | for (int i = 0; i < 5; i++) { |
89 | handles << manager.acquire(); |
90 | } |
91 | |
92 | for (uint i = 0; i < 5; i++) { |
93 | QVERIFY(!handles.at(i).isNull()); |
94 | if (i > 0) |
95 | QVERIFY(handles.at(i) != handles.at(i-1)); |
96 | } |
97 | } |
98 | |
99 | /*! |
100 | * Test that values can be properly retrieved. |
101 | */ |
102 | void tst_QResourceManager::getResources() |
103 | { |
104 | |
105 | Qt3DCore::QResourceManager<tst_ArrayResource, int> manager; |
106 | QList<tst_ArrayResource *> resources; |
107 | QList<tHandle> handles; |
108 | |
109 | for (int i = 0; i < 5; i++) { |
110 | handles << manager.acquire(); |
111 | } |
112 | |
113 | for (uint i = 0; i < 5; i++) { |
114 | resources << manager.data(handle: handles.at(i)); |
115 | QVERIFY(resources.at(i) != nullptr); |
116 | resources.at(i)->m_value = i; |
117 | } |
118 | |
119 | for (int i = 0; i < 5; i++) |
120 | QVERIFY(manager.data(handles.at(i))->m_value == i); |
121 | |
122 | // Check that an invalid resource returns NULL |
123 | tHandle iHandle; |
124 | QVERIFY(manager.data(iHandle) == nullptr); |
125 | |
126 | } |
127 | |
128 | /*! |
129 | * Test that when a resize of the data vectors in the manager occurs, |
130 | * everything behaves correctly. |
131 | */ |
132 | void tst_QResourceManager::registerResourcesResize() |
133 | { |
134 | Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager; |
135 | QList<tHandle> handles; |
136 | |
137 | for (uint i = 0; i < 2; i++) { |
138 | handles << manager.acquire(); |
139 | manager.data(handle: handles.at(i))->m_value = i + 2; |
140 | } |
141 | |
142 | for (uint i = 0; i < 5; i++) { |
143 | handles << manager.acquire(); |
144 | manager.data(handle: handles.at(i: i + 2))->m_value = i + 2 + 5; |
145 | } |
146 | |
147 | for (int i = 0; i < 7; i++) { |
148 | if (i < 2) |
149 | QVERIFY(manager.data(handles.at(i))->m_value == i + 2); |
150 | else |
151 | QVERIFY(manager.data(handles.at(i))->m_value == i + 5); |
152 | } |
153 | } |
154 | |
155 | /*! |
156 | * Checks for the removal of resources. |
157 | */ |
158 | void tst_QResourceManager::removeResource() |
159 | { |
160 | Qt3DCore::QResourceManager<tst_ArrayResource, int> manager; |
161 | |
162 | QList<tHandle> handles; |
163 | |
164 | for (int i = 0; i < 32; i++) { |
165 | handles << manager.acquire(); |
166 | } |
167 | |
168 | |
169 | tst_ArrayResource *resource = handles.at(i: 2).data(); |
170 | QVERIFY(resource != nullptr); |
171 | |
172 | manager.release(handle: handles.at(i: 2)); |
173 | QVERIFY(manager.data(handles.at(2)) == nullptr); |
174 | // Triggers QASSERT so commented |
175 | // manager.release(handles.at(2)); |
176 | |
177 | tHandle nHandle = manager.acquire(); |
178 | QVERIFY(manager.data(nHandle) != nullptr); |
179 | } |
180 | |
181 | void tst_QResourceManager::lookupResource() |
182 | { |
183 | Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager; |
184 | |
185 | QList<tst_ArrayResource *> resources; |
186 | QList<tHandle> handles; |
187 | |
188 | for (int i = 0; i < 5; i++) { |
189 | handles << manager.acquire(); |
190 | resources << manager.data(handle: handles.at(i)); |
191 | resources.at(i)->m_value = 4; |
192 | } |
193 | |
194 | tHandle t = manager.lookupHandle(id: 2); |
195 | QVERIFY(t.handle() == 0); |
196 | QVERIFY(manager.data(t) == nullptr); |
197 | tst_ArrayResource *resource = manager.getOrCreateResource(id: 2); |
198 | QVERIFY(resource != nullptr); |
199 | t = manager.lookupHandle(id: 2); |
200 | QVERIFY(manager.data(t) == manager.lookupResource(2)); |
201 | QVERIFY(t == manager.getOrAcquireHandle(2)); |
202 | QVERIFY(resource == manager.getOrCreateResource(2)); |
203 | QVERIFY(manager.data(t) == resource); |
204 | } |
205 | |
206 | void tst_QResourceManager::releaseResource() |
207 | { |
208 | Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager; |
209 | QList<tst_ArrayResource *> resources; |
210 | |
211 | for (int i = 0; i < 5; i++) { |
212 | resources << manager.getOrCreateResource(id: i); |
213 | } |
214 | |
215 | for (int i = 0; i < 5; i++) { |
216 | QVERIFY(resources.at(i) == manager.lookupResource(i)); |
217 | } |
218 | |
219 | for (int i = 0; i < 5; i++) { |
220 | manager.releaseResource(id: i); |
221 | QVERIFY(manager.lookupResource(i) == nullptr); |
222 | } |
223 | } |
224 | |
225 | class tst_Thread : public QThread |
226 | { |
227 | Q_OBJECT |
228 | public: |
229 | |
230 | typedef Qt3DCore::QResourceManager<tst_ArrayResource, |
231 | int, |
232 | Qt3DCore::ObjectLevelLockingPolicy> Manager; |
233 | |
234 | tst_Thread() |
235 | : QThread() |
236 | { |
237 | } |
238 | |
239 | void setManager(Manager *manager) |
240 | { |
241 | m_manager = manager; |
242 | } |
243 | |
244 | // QThread interface |
245 | protected: |
246 | void run() |
247 | { |
248 | int i = 0; |
249 | int max = 65535; |
250 | while (i < max) { |
251 | tst_ArrayResource *r = m_manager->getOrCreateResource(id: i); |
252 | i++; |
253 | QVERIFY(r != nullptr); |
254 | r->m_value.fetchAndAddOrdered(valueToAdd: +1); |
255 | } |
256 | qDebug() << QThread::currentThread() << "Done" ; |
257 | } |
258 | |
259 | Manager *m_manager; |
260 | }; |
261 | |
262 | void tst_QResourceManager::heavyDutyMultiThreadedAccess() |
263 | { |
264 | tst_Thread::Manager *manager = new tst_Thread::Manager(); |
265 | |
266 | QList<tst_Thread *> threads; |
267 | |
268 | int iterations = 8; |
269 | int max = 65535; |
270 | |
271 | for (int i = 0; i < iterations; i++) { |
272 | tst_Thread *thread = new tst_Thread(); |
273 | thread->setManager(manager); |
274 | threads << thread; |
275 | } |
276 | |
277 | for (int i = 0; i < iterations; i++) { |
278 | threads[i]->start(); |
279 | } |
280 | |
281 | for (int i = 0; i < iterations; i++) { |
282 | threads[i]->wait(); |
283 | } |
284 | |
285 | for (int i = 0; i < max; i++) { |
286 | QVERIFY(manager->lookupResource(i) != nullptr); |
287 | QVERIFY(manager->lookupResource(i)->m_value = iterations); |
288 | } |
289 | |
290 | qDeleteAll(c: threads); |
291 | delete manager; |
292 | } |
293 | |
294 | class tst_Thread2 : public QThread |
295 | { |
296 | Q_OBJECT |
297 | public: |
298 | |
299 | typedef Qt3DCore::QResourceManager<tst_ArrayResource, |
300 | int, |
301 | Qt3DCore::ObjectLevelLockingPolicy> Manager; |
302 | |
303 | tst_Thread2(int releaseAbove = 7) |
304 | : QThread() |
305 | , m_releaseAbove(releaseAbove) |
306 | { |
307 | } |
308 | |
309 | void setManager(Manager *manager) |
310 | { |
311 | m_manager = manager; |
312 | } |
313 | |
314 | // QThread interface |
315 | protected: |
316 | void run() |
317 | { |
318 | int i = 0; |
319 | int max = 65535; |
320 | while (i < max) { |
321 | tst_ArrayResource *r = m_manager->getOrCreateResource(id: i); |
322 | QVERIFY(r != nullptr); |
323 | int oldValue = r->m_value.fetchAndAddOrdered(valueToAdd: +1); |
324 | if (oldValue == m_releaseAbove) |
325 | m_manager->releaseResource(id: i); |
326 | i++; |
327 | } |
328 | qDebug() << QThread::currentThread() << "Done" ; |
329 | } |
330 | |
331 | Manager *m_manager; |
332 | int m_releaseAbove; |
333 | }; |
334 | |
335 | void tst_QResourceManager::heavyDutyMultiThreadedAccessRelease() |
336 | { |
337 | tst_Thread2::Manager *manager = new tst_Thread2::Manager(); |
338 | |
339 | QList<tst_Thread2 *> threads; |
340 | |
341 | int iterations = 8; |
342 | int max = 65535; |
343 | |
344 | for (int u = 0; u < 2; u++) { |
345 | |
346 | for (int i = 0; i < iterations; i++) { |
347 | tst_Thread2 *thread = new tst_Thread2(); |
348 | thread->setManager(manager); |
349 | threads << thread; |
350 | } |
351 | |
352 | for (int i = 0; i < iterations; i++) { |
353 | threads[i]->start(); |
354 | } |
355 | |
356 | for (int i = 0; i < iterations; i++) { |
357 | threads[i]->wait(); |
358 | } |
359 | |
360 | for (int i = 0; i < max; i++) { |
361 | QVERIFY(manager->lookupResource(i) == nullptr); |
362 | } |
363 | |
364 | qDeleteAll(c: threads); |
365 | threads.clear(); |
366 | } |
367 | |
368 | delete manager; |
369 | } |
370 | |
371 | void tst_QResourceManager::collectResources() |
372 | { |
373 | Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager; |
374 | |
375 | QList<tst_ArrayResource *> resources; |
376 | QList<tHandle> handles; |
377 | |
378 | for (int i = 0; i < 65536; i++) { |
379 | handles << manager.acquire(); |
380 | resources << manager.data(handle: handles.at(i)); |
381 | resources.at(i)->m_value = 4; |
382 | } |
383 | for (auto h : handles) { |
384 | manager.release(handle: h); |
385 | } |
386 | Q_ASSERT(manager.count() == 0); |
387 | handles.clear(); |
388 | manager.acquire(); |
389 | Q_ASSERT(manager.count() == 1); |
390 | } |
391 | |
392 | void tst_QResourceManager::activeHandles() |
393 | { |
394 | // GIVEN |
395 | Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager; |
396 | |
397 | { |
398 | // WHEN |
399 | const tHandle newHandle = manager.getOrAcquireHandle(id: 883U); |
400 | // THEN |
401 | QCOMPARE(manager.activeHandles().size(), 1); |
402 | QCOMPARE(manager.activeHandles()[0], newHandle); |
403 | } |
404 | |
405 | { |
406 | // WHEN |
407 | manager.releaseResource(id: 883U); |
408 | // THEN |
409 | QVERIFY(manager.activeHandles().empty()); |
410 | } |
411 | |
412 | { |
413 | // WHEN |
414 | const tHandle newHandle = manager.acquire(); |
415 | // THEN |
416 | QCOMPARE(manager.activeHandles().size(), 1); |
417 | QCOMPARE(manager.activeHandles()[0], newHandle); |
418 | |
419 | // WHEN |
420 | manager.release(handle: newHandle); |
421 | // THEN |
422 | QVERIFY(manager.activeHandles().empty()); |
423 | } |
424 | } |
425 | |
426 | void tst_QResourceManager::checkCleanup() |
427 | { |
428 | // GIVEN |
429 | Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager; |
430 | |
431 | // WHEN |
432 | tHandle newHandle = manager.getOrAcquireHandle(id: 883U); |
433 | tst_ArrayResource *data = manager.data(handle: newHandle); |
434 | |
435 | data->m_value.ref(); |
436 | // THEN |
437 | QCOMPARE(data->m_value.load(), 1); |
438 | |
439 | // WHEN |
440 | manager.release(handle: newHandle); |
441 | |
442 | // THEN |
443 | QCOMPARE(data->m_value.load(), 0); |
444 | } |
445 | |
446 | |
447 | |
448 | |
449 | QTEST_APPLESS_MAIN(tst_QResourceManager) |
450 | |
451 | #include "tst_qresourcemanager.moc" |
452 | |