GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* |
||
2 |
* The ManaPlus Client |
||
3 |
* Copyright (C) 2004-2009 The Mana World Development Team |
||
4 |
* Copyright (C) 2009-2010 The Mana Developers |
||
5 |
* Copyright (C) 2011-2019 The ManaPlus Developers |
||
6 |
* Copyright (C) 2019-2021 Andrei Karas |
||
7 |
* |
||
8 |
* This file is part of The ManaPlus Client. |
||
9 |
* |
||
10 |
* This program is free software; you can redistribute it and/or modify |
||
11 |
* it under the terms of the GNU General Public License as published by |
||
12 |
* the Free Software Foundation; either version 2 of the License, or |
||
13 |
* any later version. |
||
14 |
* |
||
15 |
* This program is distributed in the hope that it will be useful, |
||
16 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 |
* GNU General Public License for more details. |
||
19 |
* |
||
20 |
* You should have received a copy of the GNU General Public License |
||
21 |
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
22 |
*/ |
||
23 |
|||
24 |
#include "resources/resourcemanager/resourcemanager.h" |
||
25 |
|||
26 |
#ifdef USE_OPENGL |
||
27 |
#include "resources/image/image.h" |
||
28 |
#endif // USE_OPENGL |
||
29 |
|||
30 |
#include "resources/imageset.h" |
||
31 |
|||
32 |
#include "resources/memorymanager.h" |
||
33 |
|||
34 |
#include "resources/sprite/spritedef.h" |
||
35 |
|||
36 |
#include "utils/cast.h" |
||
37 |
#include "utils/checkutils.h" |
||
38 |
#include "utils/foreach.h" |
||
39 |
#include "utils/stringutils.h" |
||
40 |
|||
41 |
#if !defined(DEBUG_DUMP_LEAKS) && !defined(UNITTESTS) |
||
42 |
#include "resources/resourcetypes.h" |
||
43 |
#endif // defined(DEBUG_DUMP_LEAKS) || defined(UNITTESTS) |
||
44 |
|||
45 |
PRAGMA48(GCC diagnostic push) |
||
46 |
PRAGMA48(GCC diagnostic ignored "-Wshadow") |
||
47 |
#ifndef USE_OPENGL |
||
48 |
#include <SDL_image.h> |
||
49 |
#endif // USE_OPENGL |
||
50 |
PRAGMA48(GCC diagnostic pop) |
||
51 |
|||
52 |
#include <sstream> |
||
53 |
|||
54 |
#include <sys/time.h> |
||
55 |
|||
56 |
#include "debug.h" |
||
57 |
|||
58 |
namespace ResourceManager |
||
59 |
{ |
||
60 |
|||
61 |
1 |
std::set<SDL_Surface*> deletedSurfaces; |
|
62 |
1 |
Resources mResources; |
|
63 |
1 |
Resources mOrphanedResources; |
|
64 |
1 |
std::set<Resource*> mDeletedResources; |
|
65 |
time_t mOldestOrphan = 0; |
||
66 |
bool mDestruction = false; |
||
67 |
|||
68 |
440 |
void deleteResourceManager() |
|
69 |
{ |
||
70 |
440 |
mDestruction = true; |
|
71 |
1320 |
mResources.insert(mOrphanedResources.begin(), mOrphanedResources.end()); |
|
72 |
|||
73 |
// Release any remaining spritedefs first because they depend on image sets |
||
74 |
440 |
ResourceIterator iter = mResources.begin(); |
|
75 |
|||
76 |
#ifdef DEBUG_LEAKS |
||
77 |
#ifdef UNITTESTS |
||
78 |
bool status(false); |
||
79 |
#endif // UNITTESTS |
||
80 |
|||
81 |
while (iter != mResources.end()) |
||
82 |
{ |
||
83 |
if (iter->second) |
||
84 |
{ |
||
85 |
if (iter->second->mRefCount) |
||
86 |
{ |
||
87 |
logger->log(std::string("ResourceLeak: ").append( |
||
88 |
iter->second->mIdPath).append(" (").append( |
||
89 |
toString(iter->second->mRefCount)).append(")")); |
||
90 |
#ifdef UNITTESTS |
||
91 |
status = true; |
||
92 |
#endif // UNITTESTS |
||
93 |
} |
||
94 |
} |
||
95 |
++iter; |
||
96 |
} |
||
97 |
|||
98 |
#ifdef UNITTESTS |
||
99 |
if (status) |
||
100 |
reportAlways("Found leaked resources.") |
||
101 |
#endif // UNITTESTS |
||
102 |
|||
103 |
iter = mResources.begin(); |
||
104 |
#endif // DEBUG_LEAKS |
||
105 |
|||
106 |
✗✓ | 440 |
while (iter != mResources.end()) |
107 |
{ |
||
108 |
#ifdef DEBUG_LEAKS |
||
109 |
if (iter->second && iter->second->mRefCount) |
||
110 |
{ |
||
111 |
++iter; |
||
112 |
continue; |
||
113 |
} |
||
114 |
#endif // DEBUG_LEAKS |
||
115 |
|||
116 |
if (dynamic_cast<SpriteDef*>(iter->second) != nullptr) |
||
117 |
{ |
||
118 |
cleanUp(iter->second); |
||
119 |
const ResourceIterator toErase = iter; |
||
120 |
++iter; |
||
121 |
mResources.erase(toErase); |
||
122 |
} |
||
123 |
else |
||
124 |
{ |
||
125 |
++iter; |
||
126 |
} |
||
127 |
} |
||
128 |
|||
129 |
// Release any remaining image sets first because they depend on images |
||
130 |
440 |
iter = mResources.begin(); |
|
131 |
✗✓ | 440 |
while (iter != mResources.end()) |
132 |
{ |
||
133 |
#ifdef DEBUG_LEAKS |
||
134 |
if (iter->second && iter->second->mRefCount) |
||
135 |
{ |
||
136 |
++iter; |
||
137 |
continue; |
||
138 |
} |
||
139 |
#endif // DEBUG_LEAKS |
||
140 |
|||
141 |
if (dynamic_cast<ImageSet*>(iter->second) != nullptr) |
||
142 |
{ |
||
143 |
cleanUp(iter->second); |
||
144 |
const ResourceIterator toErase = iter; |
||
145 |
++iter; |
||
146 |
mResources.erase(toErase); |
||
147 |
} |
||
148 |
else |
||
149 |
{ |
||
150 |
++iter; |
||
151 |
} |
||
152 |
} |
||
153 |
|||
154 |
// Release remaining resources, logging the number of dangling references. |
||
155 |
440 |
iter = mResources.begin(); |
|
156 |
✗✓ | 440 |
while (iter != mResources.end()) |
157 |
{ |
||
158 |
#ifdef DEBUG_LEAKS |
||
159 |
if (iter->second && iter->second->mRefCount) |
||
160 |
{ |
||
161 |
++iter; |
||
162 |
continue; |
||
163 |
} |
||
164 |
#endif // DEBUG_LEAKS |
||
165 |
|||
166 |
if (iter->second != nullptr) |
||
167 |
{ |
||
168 |
cleanUp(iter->second); |
||
169 |
const ResourceIterator toErase = iter; |
||
170 |
++iter; |
||
171 |
mResources.erase(toErase); |
||
172 |
} |
||
173 |
else |
||
174 |
{ |
||
175 |
++iter; |
||
176 |
} |
||
177 |
} |
||
178 |
440 |
clearDeleted(true); |
|
179 |
440 |
clearScheduled(); |
|
180 |
440 |
mDestruction = false; |
|
181 |
440 |
} |
|
182 |
|||
183 |
1 |
void cleanUp(Resource *const res) |
|
184 |
{ |
||
185 |
✓✗ | 1 |
if (res == nullptr) |
186 |
return; |
||
187 |
|||
188 |
1 |
const unsigned refCount = res->mRefCount; |
|
189 |
✗✓ | 1 |
if (refCount > 0) |
190 |
{ |
||
191 |
logger->log("ResourceManager::~ResourceManager() cleaning up %u " |
||
192 |
"reference%s to %s", |
||
193 |
refCount, |
||
194 |
(refCount == 1) ? "" : "s", |
||
195 |
res->mIdPath.c_str()); |
||
196 |
} |
||
197 |
|||
198 |
1 |
delete res; |
|
199 |
#ifdef DEBUG_LEAKS |
||
200 |
cleanOrphans(true); |
||
201 |
#endif // DEBUG_LEAKS |
||
202 |
} |
||
203 |
|||
204 |
441 |
void cleanProtected() |
|
205 |
{ |
||
206 |
441 |
ResourceIterator iter = mResources.begin(); |
|
207 |
✓✓ | 3149 |
while (iter != mResources.end()) |
208 |
{ |
||
209 |
2708 |
Resource *const res = iter->second; |
|
210 |
✗✓ | 2708 |
if (res == nullptr) |
211 |
{ |
||
212 |
++ iter; |
||
213 |
continue; |
||
214 |
} |
||
215 |
✓✓ | 2708 |
if (res->mProtected) |
216 |
{ |
||
217 |
1 |
res->mProtected = false; |
|
218 |
1 |
res->decRef(); |
|
219 |
1 |
iter = mResources.begin(); |
|
220 |
1 |
continue; |
|
221 |
} |
||
222 |
|||
223 |
++ iter; |
||
224 |
} |
||
225 |
441 |
} |
|
226 |
|||
227 |
1234 |
bool cleanOrphans(const bool always) |
|
228 |
{ |
||
229 |
timeval tv; |
||
230 |
1234 |
gettimeofday(&tv, nullptr); |
|
231 |
// Delete orphaned resources after 30 seconds. |
||
232 |
1234 |
time_t oldest = static_cast<time_t>(tv.tv_sec); |
|
233 |
1234 |
const time_t threshold = oldest - 30; |
|
234 |
|||
235 |
✓✓✓✓ ✓✓✓✓ |
1234 |
if (mOrphanedResources.empty() || (!always && mOldestOrphan >= threshold)) |
236 |
return false; |
||
237 |
|||
238 |
682 |
bool status(false); |
|
239 |
682 |
ResourceIterator iter = mOrphanedResources.begin(); |
|
240 |
✓✓ | 11451 |
while (iter != mOrphanedResources.end()) |
241 |
{ |
||
242 |
10769 |
Resource *const res = iter->second; |
|
243 |
✗✓ | 10769 |
if (res == nullptr) |
244 |
{ |
||
245 |
++iter; |
||
246 |
continue; |
||
247 |
} |
||
248 |
10769 |
const time_t t = res->mTimeStamp; |
|
249 |
✗✓ | 10769 |
if (!always && t >= threshold) |
250 |
{ |
||
251 |
if (t < oldest) |
||
252 |
oldest = t; |
||
253 |
++ iter; |
||
254 |
} |
||
255 |
else |
||
256 |
{ |
||
257 |
10769 |
logResource(res); |
|
258 |
10769 |
const ResourceIterator toErase = iter; |
|
259 |
10769 |
++iter; |
|
260 |
10769 |
mOrphanedResources.erase(toErase); |
|
261 |
10769 |
delete res; // delete only after removal from list, |
|
262 |
// to avoid issues in recursion |
||
263 |
10769 |
status = true; |
|
264 |
} |
||
265 |
} |
||
266 |
|||
267 |
682 |
mOldestOrphan = oldest; |
|
268 |
682 |
return status; |
|
269 |
} |
||
270 |
|||
271 |
10772 |
void logResource(const Resource *const res) |
|
272 |
{ |
||
273 |
✓✗ | 10772 |
if (res == nullptr) |
274 |
return; |
||
275 |
#ifdef USE_OPENGL |
||
276 |
10772 |
const Image *const image = dynamic_cast<const Image *>(res); |
|
277 |
✓✓ | 10772 |
if (image != nullptr) |
278 |
{ |
||
279 |
17688 |
std::string src = image->mSource; |
|
280 |
5896 |
const int count = image->mRefCount; |
|
281 |
✗✓ | 5896 |
if (count != 0) |
282 |
src.append(" ").append(toString(count)); |
||
283 |
17688 |
logger->log("resource(%s, %u) %s", res->mIdPath.c_str(), |
|
284 |
✓✗ | 5896 |
image->getGLImage(), src.c_str()); |
285 |
} |
||
286 |
else |
||
287 |
{ |
||
288 |
14628 |
std::string src = res->mSource; |
|
289 |
4876 |
const int count = res->mRefCount; |
|
290 |
✓✓ | 4876 |
if (count > 0) |
291 |
✓✗✓✗ |
9 |
src.append(" ").append(toString(count)); |
292 |
✓✗ | 9752 |
logger->log("resource(%s) %s", res->mIdPath.c_str(), src.c_str()); |
293 |
} |
||
294 |
#else // USE_OPENGL |
||
295 |
|||
296 |
logger->log("resource(%s)", res->mIdPath.c_str()); |
||
297 |
#endif // USE_OPENGL |
||
298 |
} |
||
299 |
|||
300 |
void logResources(const std::string &msg) |
||
301 |
{ |
||
302 |
logger->log("start of resources: " + msg); |
||
303 |
logger->log("resources"); |
||
304 |
FOR_EACH(ResourceIterator, it, mResources) |
||
305 |
{ |
||
306 |
logResource((*it).second); |
||
307 |
} |
||
308 |
logger->log("orphaned resources"); |
||
309 |
FOR_EACH(ResourceIterator, it, mOrphanedResources) |
||
310 |
{ |
||
311 |
logResource((*it).second); |
||
312 |
} |
||
313 |
logger->log("deleted resources"); |
||
314 |
FOR_EACH(std::set<Resource*>::iterator, it, mDeletedResources) |
||
315 |
{ |
||
316 |
logResource(*it); |
||
317 |
} |
||
318 |
logger->log("end of resources"); |
||
319 |
} |
||
320 |
|||
321 |
442 |
void clearDeleted(const bool full) |
|
322 |
{ |
||
323 |
442 |
bool status(true); |
|
324 |
442 |
logger->log1("clear deleted"); |
|
325 |
✓✓ | 884 |
while (status) |
326 |
{ |
||
327 |
442 |
status = false; |
|
328 |
442 |
std::set<Resource*>::iterator resDelIter = mDeletedResources.begin(); |
|
329 |
✓✓ | 443 |
while (resDelIter != mDeletedResources.end()) |
330 |
{ |
||
331 |
✗✓ | 1 |
if ((*resDelIter)->mRefCount == 0U) |
332 |
{ |
||
333 |
status = true; |
||
334 |
Resource *res = *resDelIter; |
||
335 |
logResource(res); |
||
336 |
mDeletedResources.erase(resDelIter); |
||
337 |
delete res; |
||
338 |
break; |
||
339 |
} |
||
340 |
++ resDelIter; |
||
341 |
} |
||
342 |
} |
||
343 |
✓✗✓✓ ✓✓ |
884 |
if (full && !mDeletedResources.empty()) |
344 |
{ |
||
345 |
1 |
logger->log1("leaks in deleted"); |
|
346 |
1 |
std::set<Resource*>::iterator resDelIter = mDeletedResources.begin(); |
|
347 |
✓✓ | 2 |
while (resDelIter != mDeletedResources.end()) |
348 |
{ |
||
349 |
1 |
logResource(*resDelIter); |
|
350 |
|||
351 |
// for debug only |
||
352 |
// delete *resDelIter; |
||
353 |
// for debug only |
||
354 |
|||
355 |
++ resDelIter; |
||
356 |
} |
||
357 |
} |
||
358 |
442 |
} |
|
359 |
|||
360 |
1 |
bool addResource(const std::string &idPath, |
|
361 |
Resource *const resource) |
||
362 |
{ |
||
363 |
✓✗ | 1 |
if (resource != nullptr) |
364 |
{ |
||
365 |
1 |
resource->incRef(); |
|
366 |
2 |
resource->mIdPath = idPath; |
|
367 |
#ifdef DEBUG_IMAGES |
||
368 |
logger->log("set name %p, %s", static_cast<void*>(resource), |
||
369 |
resource->mIdPath.c_str()); |
||
370 |
#endif // DEBUG_IMAGES |
||
371 |
|||
372 |
1 |
mResources[idPath] = resource; |
|
373 |
1 |
return true; |
|
374 |
} |
||
375 |
return false; |
||
376 |
} |
||
377 |
|||
378 |
Resource *getFromCache(const std::string &filename, |
||
379 |
const int variant) |
||
380 |
{ |
||
381 |
std::stringstream ss; |
||
382 |
ss << filename << "[" << variant << "]"; |
||
383 |
return getFromCache(ss.str()); |
||
384 |
} |
||
385 |
|||
386 |
6 |
bool isInCache(const std::string &idPath) |
|
387 |
{ |
||
388 |
6 |
const ResourceCIterator &resIter = mResources.find(idPath); |
|
389 |
✓✓✗✓ |
15 |
return (resIter != mResources.end() && (resIter->second != nullptr)); |
390 |
} |
||
391 |
|||
392 |
3 |
Resource *getTempResource(const std::string &idPath) |
|
393 |
{ |
||
394 |
3 |
const ResourceCIterator &resIter = mResources.find(idPath); |
|
395 |
✓✓ | 3 |
if (resIter != mResources.end()) |
396 |
{ |
||
397 |
1 |
Resource *const res = resIter->second; |
|
398 |
✗✓ | 1 |
if (resIter->second != nullptr) |
399 |
return res; |
||
400 |
} |
||
401 |
return nullptr; |
||
402 |
} |
||
403 |
|||
404 |
22194 |
Resource *getFromCache(const std::string &idPath) |
|
405 |
{ |
||
406 |
// Check if the id exists, and return the value if it does. |
||
407 |
22194 |
ResourceIterator resIter = mResources.find(idPath); |
|
408 |
✓✓ | 22194 |
if (resIter != mResources.end()) |
409 |
{ |
||
410 |
✓✗ | 11366 |
if (resIter->second != nullptr) |
411 |
11366 |
resIter->second->incRef(); |
|
412 |
11366 |
return resIter->second; |
|
413 |
} |
||
414 |
|||
415 |
10828 |
resIter = mOrphanedResources.find(idPath); |
|
416 |
✓✓ | 10828 |
if (resIter != mOrphanedResources.end()) |
417 |
{ |
||
418 |
53 |
Resource *const res = resIter->second; |
|
419 |
106 |
mResources.insert(*resIter); |
|
420 |
53 |
mOrphanedResources.erase(resIter); |
|
421 |
✓✗ | 53 |
if (res != nullptr) |
422 |
53 |
res->incRef(); |
|
423 |
return res; |
||
424 |
} |
||
425 |
return nullptr; |
||
426 |
} |
||
427 |
|||
428 |
22190 |
Resource *get(const std::string &idPath, |
|
429 |
generator fun, |
||
430 |
const void *const data) |
||
431 |
{ |
||
432 |
#ifndef DISABLE_RESOURCE_CACHING |
||
433 |
22190 |
Resource *resource = getFromCache(idPath); |
|
434 |
✓✓ | 22190 |
if (resource != nullptr) |
435 |
return resource; |
||
436 |
10775 |
resource = fun(data); |
|
437 |
|||
438 |
✓✗ | 10775 |
if (resource != nullptr) |
439 |
{ |
||
440 |
10775 |
resource->incRef(); |
|
441 |
21550 |
resource->mIdPath = idPath; |
|
442 |
#ifdef DEBUG_IMAGES |
||
443 |
logger->log("set name %p, %s", static_cast<void*>(resource), |
||
444 |
resource->mIdPath.c_str()); |
||
445 |
#endif // DEBUG_IMAGES |
||
446 |
|||
447 |
10775 |
mResources[idPath] = resource; |
|
448 |
} |
||
449 |
else |
||
450 |
{ |
||
451 |
reportAlways("Error loading resource: %s", idPath.c_str()) |
||
452 |
} |
||
453 |
#else // DISABLE_RESOURCE_CACHING |
||
454 |
|||
455 |
Resource *resource = fun(data, idPath); |
||
456 |
|||
457 |
if (resource) |
||
458 |
{ |
||
459 |
resource->incRef(); |
||
460 |
resource->mIdPath = idPath; |
||
461 |
#ifdef DEBUG_IMAGES |
||
462 |
logger->log("set name %p, %s", static_cast<void*>(resource), |
||
463 |
resource->mIdPath.c_str()); |
||
464 |
#endif // DEBUG_IMAGES |
||
465 |
} |
||
466 |
else |
||
467 |
{ |
||
468 |
reportAlways("Error loading resource: " + idPath) |
||
469 |
} |
||
470 |
#endif // DISABLE_RESOURCE_CACHING |
||
471 |
|||
472 |
// Returns nullptr if the object could not be created. |
||
473 |
10775 |
return resource; |
|
474 |
} |
||
475 |
|||
476 |
10828 |
void release(Resource *const res) |
|
477 |
{ |
||
478 |
✓✗✓✗ |
10828 |
if ((res == nullptr) || mDestruction) |
479 |
3 |
return; |
|
480 |
|||
481 |
#ifndef DISABLE_RESOURCE_CACHING |
||
482 |
10828 |
std::set<Resource*>::iterator resDelIter = mDeletedResources.find(res); |
|
483 |
✓✓ | 10828 |
if (resDelIter != mDeletedResources.end()) |
484 |
{ |
||
485 |
// we found zero counted image in deleted list. deleting it and exit. |
||
486 |
3 |
mDeletedResources.erase(resDelIter); |
|
487 |
3 |
delete res; |
|
488 |
return; |
||
489 |
} |
||
490 |
|||
491 |
21650 |
ResourceIterator resIter = mResources.find(res->mIdPath); |
|
492 |
|||
493 |
✗✓ | 10825 |
if (resIter == mResources.end()) |
494 |
{ |
||
495 |
// +++ need reenable after Resource will have type field |
||
496 |
// reportAlways("no resource in cache: %s", |
||
497 |
// res->mIdPath.c_str()) |
||
498 |
delete res; |
||
499 |
return; |
||
500 |
} |
||
501 |
✗✓ | 10825 |
if (resIter->second != res) |
502 |
{ |
||
503 |
// +++ need reenable after Resource will have type field |
||
504 |
// reportAlways("in cache other image: %s", |
||
505 |
// res->mIdPath.c_str()) |
||
506 |
delete res; |
||
507 |
return; |
||
508 |
} |
||
509 |
|||
510 |
timeval tv; |
||
511 |
10825 |
gettimeofday(&tv, nullptr); |
|
512 |
10825 |
const time_t timestamp = static_cast<time_t>(tv.tv_sec); |
|
513 |
|||
514 |
10825 |
res->mTimeStamp = timestamp; |
|
515 |
✓✓ | 10825 |
if (mOrphanedResources.empty()) |
516 |
279 |
mOldestOrphan = timestamp; |
|
517 |
|||
518 |
21650 |
mOrphanedResources.insert(*resIter); |
|
519 |
10825 |
mResources.erase(resIter); |
|
520 |
#else // DISABLE_RESOURCE_CACHING |
||
521 |
|||
522 |
delete res; |
||
523 |
#endif // DISABLE_RESOURCE_CACHING |
||
524 |
} |
||
525 |
|||
526 |
5 |
void moveToDeleted(Resource *const res) |
|
527 |
{ |
||
528 |
✓✗ | 5 |
if (res == nullptr) |
529 |
return; |
||
530 |
|||
531 |
5 |
bool found(false); |
|
532 |
5 |
const int count = res->mRefCount; |
|
533 |
✓✓ | 5 |
if (count == 1) |
534 |
1 |
logResource(res); |
|
535 |
5 |
res->decRef(); |
|
536 |
10 |
ResourceIterator resIter = mResources.find(res->mIdPath); |
|
537 |
✓✓✗✓ ✓✓ |
13 |
if (resIter != mResources.end() && resIter->second == res) |
538 |
{ |
||
539 |
3 |
mResources.erase(resIter); |
|
540 |
3 |
found = true; |
|
541 |
} |
||
542 |
else |
||
543 |
{ |
||
544 |
4 |
resIter = mOrphanedResources.find(res->mIdPath); |
|
545 |
✓✗✗✓ ✓✗ |
6 |
if (resIter != mOrphanedResources.end() && resIter->second == res) |
546 |
{ |
||
547 |
2 |
mOrphanedResources.erase(resIter); |
|
548 |
2 |
found = true; |
|
549 |
} |
||
550 |
} |
||
551 |
✓✗ | 5 |
if (found) |
552 |
{ |
||
553 |
✓✓ | 5 |
if (count > 1) |
554 |
mDeletedResources.insert(res); |
||
555 |
else |
||
556 |
✓✗ | 2 |
delete res; |
557 |
} |
||
558 |
} |
||
559 |
|||
560 |
1 |
void decRefDelete(Resource *const res) |
|
561 |
{ |
||
562 |
✓✗ | 1 |
if (res == nullptr) |
563 |
return; |
||
564 |
|||
565 |
1 |
const int count = res->mRefCount; |
|
566 |
✓✗ | 1 |
if (count == 1) |
567 |
{ |
||
568 |
1 |
logResource(res); |
|
569 |
|||
570 |
2 |
ResourceIterator resIter = mResources.find(res->mIdPath); |
|
571 |
✓✗✗✓ ✓✗ |
3 |
if (resIter != mResources.end() && resIter->second == res) |
572 |
{ |
||
573 |
mResources.erase(resIter); |
||
574 |
} |
||
575 |
else |
||
576 |
{ |
||
577 |
resIter = mOrphanedResources.find(res->mIdPath); |
||
578 |
if (resIter != mOrphanedResources.end() && resIter->second == res) |
||
579 |
mOrphanedResources.erase(resIter); |
||
580 |
} |
||
581 |
|||
582 |
1 |
delete res; |
|
583 |
} |
||
584 |
else |
||
585 |
{ |
||
586 |
res->decRef(); |
||
587 |
} |
||
588 |
} |
||
589 |
|||
590 |
440 |
void deleteInstance() |
|
591 |
{ |
||
592 |
#ifdef DUMP_LEAKED_RESOURCES |
||
593 |
440 |
logger->log1("clean orphans start"); |
|
594 |
440 |
ResourceManager::cleanProtected(); |
|
595 |
✓✓ | 1052 |
while (ResourceManager::cleanOrphans(true)) |
596 |
continue; |
||
597 |
440 |
logger->log1("clean orphans end"); |
|
598 |
440 |
ResourceIterator iter = ResourceManager::mResources.begin(); |
|
599 |
|||
600 |
#ifdef UNITTESTS |
||
601 |
440 |
bool status(false); |
|
602 |
#endif // UNITTESTS |
||
603 |
|||
604 |
✗✓ | 440 |
while (iter != ResourceManager::mResources.end()) |
605 |
{ |
||
606 |
const Resource *const res = iter->second; |
||
607 |
if (res != nullptr) |
||
608 |
{ |
||
609 |
if (res->mRefCount != 0U) |
||
610 |
{ |
||
611 |
logger->log(std::string("ResourceLeak: ").append( |
||
612 |
res->mIdPath).append(" (").append(toString( |
||
613 |
res->mRefCount)).append(")")); |
||
614 |
#ifdef UNITTESTS |
||
615 |
status = true; |
||
616 |
#endif // UNITTESTS |
||
617 |
} |
||
618 |
} |
||
619 |
++iter; |
||
620 |
} |
||
621 |
#ifdef UNITTESTS |
||
622 |
✗✓ | 440 |
if (status) |
623 |
reportAlways("Found leaked resources.") |
||
624 |
#endif // UNITTESTS |
||
625 |
#endif // DUMP_LEAKED_RESOURCES |
||
626 |
|||
627 |
440 |
deleteResourceManager(); |
|
628 |
440 |
} |
|
629 |
|||
630 |
void scheduleDelete(SDL_Surface *const surface) |
||
631 |
{ |
||
632 |
deletedSurfaces.insert(surface); |
||
633 |
} |
||
634 |
|||
635 |
440 |
void clearScheduled() |
|
636 |
{ |
||
637 |
BLOCK_START("ResourceManager::clearScheduled") |
||
638 |
✗✓ | 880 |
FOR_EACH (std::set<SDL_Surface*>::iterator, i, deletedSurfaces) |
639 |
MSDL_FreeSurface(*i); |
||
640 |
440 |
deletedSurfaces.clear(); |
|
641 |
BLOCK_END("ResourceManager::clearScheduled") |
||
642 |
440 |
} |
|
643 |
|||
644 |
void clearCache() |
||
645 |
{ |
||
646 |
cleanProtected(); |
||
647 |
while (cleanOrphans(true)) |
||
648 |
continue; |
||
649 |
} |
||
650 |
|||
651 |
int calcMemoryLocal() |
||
652 |
{ |
||
653 |
int sz = 24; |
||
654 |
FOR_EACH (std::set<SDL_Surface*>::iterator, it, deletedSurfaces) |
||
655 |
{ |
||
656 |
sz += MemoryManager::getSurfaceSize(*it); |
||
657 |
} |
||
658 |
return sz; |
||
659 |
} |
||
660 |
|||
661 |
int calcMemoryChilds(const int level) |
||
662 |
{ |
||
663 |
int sz = 0; |
||
664 |
FOR_EACH (ResourceCIterator, it, mResources) |
||
665 |
{ |
||
666 |
sz += static_cast<int>((*it).first.capacity()); |
||
667 |
sz += (*it).second->calcMemory(level + 1); |
||
668 |
} |
||
669 |
FOR_EACH (ResourceCIterator, it, mOrphanedResources) |
||
670 |
{ |
||
671 |
sz += static_cast<int>((*it).first.capacity()); |
||
672 |
sz += (*it).second->calcMemory(level + 1); |
||
673 |
} |
||
674 |
FOR_EACH (std::set<Resource*>::const_iterator, it, mDeletedResources) |
||
675 |
{ |
||
676 |
sz += (*it)->calcMemory(level + 1); |
||
677 |
} |
||
678 |
return sz; |
||
679 |
} |
||
680 |
|||
681 |
int calcMemory(const int level) |
||
682 |
{ |
||
683 |
const int sumLocal = calcMemoryLocal(); |
||
684 |
const int sumChilds = calcMemoryChilds(0); |
||
685 |
MemoryManager::printMemory("resource manager", |
||
686 |
level, |
||
687 |
sumLocal, |
||
688 |
sumChilds); |
||
689 |
return sumLocal + sumChilds; |
||
690 |
} |
||
691 |
|||
692 |
int size() noexcept2 |
||
693 |
{ |
||
694 |
return CAST_S32(mResources.size()); |
||
695 |
} |
||
696 |
|||
697 |
#if defined(DEBUG_DUMP_LEAKS) || defined(UNITTESTS) |
||
698 |
98 |
Resources &getResources() |
|
699 |
{ |
||
700 |
98 |
return mResources; |
|
701 |
} |
||
702 |
|||
703 |
49 |
Resources &getOrphanedResources() |
|
704 |
{ |
||
705 |
49 |
return mOrphanedResources; |
|
706 |
} |
||
707 |
|||
708 |
45 |
const std::set<Resource*> &getDeletedResources() |
|
709 |
{ |
||
710 |
45 |
return mDeletedResources; |
|
711 |
} |
||
712 |
#endif // defined(DEBUG_DUMP_LEAKS) || defined(UNITTESTS) |
||
713 |
|||
714 |
✓✗✓✗ |
3 |
} // namespace ResourceManager |
Generated by: GCOVR (Version 3.3) |