Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: GDAL Core
5 : * Purpose: Test block cache under multi-threading
6 : * Author: Even Rouault, <even dot rouault at spatialys dot com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #ifndef DEBUG
31 : #define DEBUG
32 : #endif
33 :
34 : #include "cpl_multiproc.h"
35 : #include "gdal_priv.h"
36 :
37 : #include <cstdlib>
38 : #include <vector>
39 :
40 : #include "gtest_include.h"
41 :
42 : extern int global_argc;
43 : extern char **global_argv;
44 :
45 : namespace
46 : {
47 :
48 : CPLLock *psLock = nullptr;
49 :
50 0 : static void Usage()
51 : {
52 0 : printf("Usage: testblockcache [-threads X] [-loops X] [-max_requests X] "
53 : "[-strategy random|line|block]\n");
54 0 : printf(" [-migrate] [ filename |\n");
55 0 : printf(" [[-xsize val] [-ysize val] [-bands val] "
56 : "[-co key=value]*\n");
57 0 : printf(" [[-memdriver] | [-ondisk]] [-check]] ]\n");
58 0 : exit(1);
59 : }
60 :
61 : int nLoops = 1;
62 : const char *pszDataset = nullptr;
63 : int bCheck = FALSE;
64 :
65 : typedef enum
66 : {
67 : STRATEGY_RANDOM,
68 : STRATEGY_LINE,
69 : STRATEGY_BLOCK,
70 : } Strategy;
71 :
72 : typedef struct _Request Request;
73 :
74 : struct _Request
75 : {
76 : int nXOff, nYOff, nXWin, nYWin;
77 : int nBands;
78 : Request *psNext;
79 : };
80 :
81 : typedef struct _Resource Resource;
82 :
83 : struct _Resource
84 : {
85 : GDALDataset *poDS;
86 : void *pBuffer;
87 : Resource *psNext;
88 : Resource *psPrev;
89 : };
90 :
91 : typedef struct
92 : {
93 : GDALDataset *poDS;
94 : Request *psRequestList;
95 : int nBufferSize;
96 : } ThreadDescription;
97 :
98 : static Request *psGlobalRequestList = nullptr;
99 : static Resource *psGlobalResourceList = nullptr;
100 : static Resource *psGlobalResourceLast = nullptr;
101 :
102 : /* according to rand() man page, POSIX.1-2001 proposes the following
103 : * implementation */
104 : /* RAND_MAX assumed to be 32767 */
105 : #define MYRAND_MAX 32767
106 :
107 5453030000 : static int myrand_r(unsigned long *pseed)
108 : {
109 5453030000 : *pseed = *pseed * 1103515245 + 12345;
110 5453030000 : return ((unsigned)((*pseed / 65536UL) % (MYRAND_MAX + 1)));
111 : }
112 :
113 22400 : static void Check(GByte *pBuffer, int nXSize, int nYSize, int nBands, int nXOff,
114 : int nYOff, int nXWin, int nYWin)
115 : {
116 112000 : for (int iBand = 0; iBand < nBands; iBand++)
117 : {
118 21681600 : for (int iY = 0; iY < nYWin; iY++)
119 : {
120 5157160000 : for (int iX = 0; iX < nXWin; iX++)
121 : {
122 5135570000 : unsigned long seed = iBand * nXSize * nYSize +
123 5135570000 : (iY + nYOff) * nXSize + iX + nXOff;
124 5135570000 : GByte expected = (GByte)(myrand_r(&seed) & 0xff);
125 5250460000 : EXPECT_EQ(pBuffer[iBand * nXWin * nYWin + iY * nXWin + iX],
126 : expected);
127 : }
128 : }
129 : }
130 22400 : }
131 :
132 22400 : static void ReadRaster(GDALDataset *poDS, int nXSize, int nYSize, int nBands,
133 : GByte *pBuffer, int nXOff, int nYOff, int nXWin,
134 : int nYWin)
135 : {
136 22400 : CPL_IGNORE_RET_VAL(poDS->RasterIO(GF_Read, nXOff, nYOff, nXWin, nYWin,
137 : pBuffer, nXWin, nYWin, GDT_Byte, nBands,
138 : nullptr, 0, 0, 0
139 : #ifdef GDAL_COMPILATION
140 : ,
141 : nullptr
142 : #endif
143 : ));
144 22400 : if (bCheck)
145 : {
146 22400 : Check(pBuffer, nXSize, nYSize, nBands, nXOff, nYOff, nXWin, nYWin);
147 : }
148 22400 : }
149 :
150 22400 : static void AddRequest(Request *&psRequestList, Request *&psRequestLast,
151 : int nXOff, int nYOff, int nXWin, int nYWin, int nBands)
152 : {
153 22400 : Request *psRequest = (Request *)CPLMalloc(sizeof(Request));
154 22400 : psRequest->nXOff = nXOff;
155 22400 : psRequest->nYOff = nYOff;
156 22400 : psRequest->nXWin = nXWin;
157 22400 : psRequest->nYWin = nYWin;
158 22400 : psRequest->nBands = nBands;
159 22400 : if (psRequestLast)
160 22379 : psRequestLast->psNext = psRequest;
161 : else
162 21 : psRequestList = psRequest;
163 22400 : psRequestLast = psRequest;
164 22400 : psRequest->psNext = nullptr;
165 22400 : }
166 :
167 22404 : static Request *GetNextRequest(Request *&psRequestList)
168 : {
169 22404 : if (psLock)
170 1604 : CPLAcquireLock(psLock);
171 22404 : Request *psRet = psRequestList;
172 22404 : if (psRequestList)
173 : {
174 22400 : psRequestList = psRequestList->psNext;
175 22400 : psRet->psNext = nullptr;
176 : }
177 22404 : if (psLock)
178 1604 : CPLReleaseLock(psLock);
179 22404 : return psRet;
180 : }
181 :
182 1600 : static Resource *AcquireFirstResource()
183 : {
184 1600 : if (psLock)
185 1600 : CPLAcquireLock(psLock);
186 1600 : Resource *psRet = psGlobalResourceList;
187 1600 : psGlobalResourceList = psGlobalResourceList->psNext;
188 1600 : if (psGlobalResourceList)
189 3 : psGlobalResourceList->psPrev = nullptr;
190 : else
191 1597 : psGlobalResourceLast = nullptr;
192 1600 : psRet->psNext = nullptr;
193 1600 : CPLAssert(psRet->psPrev == nullptr);
194 1600 : if (psLock)
195 1600 : CPLReleaseLock(psLock);
196 1600 : return psRet;
197 : }
198 :
199 1604 : static void PutResourceAtEnd(Resource *psResource)
200 : {
201 1604 : if (psLock)
202 1600 : CPLAcquireLock(psLock);
203 1604 : psResource->psPrev = psGlobalResourceLast;
204 1604 : psResource->psNext = nullptr;
205 1604 : if (psGlobalResourceList == nullptr)
206 1598 : psGlobalResourceList = psResource;
207 : else
208 6 : psGlobalResourceLast->psNext = psResource;
209 1604 : psGlobalResourceLast = psResource;
210 1604 : if (psLock)
211 1600 : CPLReleaseLock(psLock);
212 1604 : }
213 :
214 20 : static void ThreadFuncDedicatedDataset(void *_psThreadDescription)
215 : {
216 20 : ThreadDescription *psThreadDescription =
217 : (ThreadDescription *)_psThreadDescription;
218 20 : int nXSize = psThreadDescription->poDS->GetRasterXSize();
219 20 : int nYSize = psThreadDescription->poDS->GetRasterYSize();
220 20 : void *pBuffer = CPLMalloc(psThreadDescription->nBufferSize);
221 20820 : while (psThreadDescription->psRequestList != nullptr)
222 : {
223 20800 : Request *psRequest = GetNextRequest(psThreadDescription->psRequestList);
224 20800 : ReadRaster(psThreadDescription->poDS, nXSize, nYSize, psRequest->nBands,
225 : (GByte *)pBuffer, psRequest->nXOff, psRequest->nYOff,
226 : psRequest->nXWin, psRequest->nYWin);
227 20800 : CPLFree(psRequest);
228 : }
229 20 : CPLFree(pBuffer);
230 20 : }
231 :
232 1604 : static void ThreadFuncWithMigration(void * /* _unused */)
233 : {
234 : Request *psRequest;
235 1604 : while ((psRequest = GetNextRequest(psGlobalRequestList)) != nullptr)
236 : {
237 1600 : Resource *psResource = AcquireFirstResource();
238 1600 : ASSERT_TRUE(psResource != nullptr);
239 1600 : int nXSize = psResource->poDS->GetRasterXSize();
240 1600 : int nYSize = psResource->poDS->GetRasterYSize();
241 1600 : ReadRaster(psResource->poDS, nXSize, nYSize, psRequest->nBands,
242 1600 : (GByte *)psResource->pBuffer, psRequest->nXOff,
243 : psRequest->nYOff, psRequest->nXWin, psRequest->nYWin);
244 1600 : CPLFree(psRequest);
245 1600 : PutResourceAtEnd(psResource);
246 : }
247 : }
248 :
249 24 : static int CreateRandomStrategyRequests(GDALDataset *poDS, int nMaxRequests,
250 : Request *&psRequestList,
251 : Request *&psRequestLast)
252 : {
253 24 : unsigned long seed = 1;
254 24 : int nXSize = poDS->GetRasterXSize();
255 24 : int nYSize = poDS->GetRasterYSize();
256 24 : int nMaxXWin = MIN(1000, nXSize / 10 + 1);
257 24 : int nMaxYWin = MIN(1000, nYSize / 10 + 1);
258 24 : int nQueriedBands = MIN(4, poDS->GetRasterCount());
259 24 : int nAverageIterationsToReadWholeFile =
260 24 : ((nXSize + nMaxXWin / 2 - 1) / (nMaxXWin / 2)) *
261 24 : ((nYSize + nMaxYWin / 2 - 1) / (nMaxYWin / 2));
262 24 : int nLocalLoops = nLoops * nAverageIterationsToReadWholeFile;
263 22424 : for (int iLoop = 0; iLoop < nLocalLoops; iLoop++)
264 : {
265 22400 : if (nMaxRequests > 0 && iLoop == nMaxRequests)
266 0 : break;
267 22400 : int nXOff = (int)((GIntBig)myrand_r(&seed) * (nXSize - 1) / MYRAND_MAX);
268 22400 : int nYOff = (int)((GIntBig)myrand_r(&seed) * (nYSize - 1) / MYRAND_MAX);
269 22400 : int nXWin = 1 + (int)((GIntBig)myrand_r(&seed) * nMaxXWin / MYRAND_MAX);
270 22400 : int nYWin = 1 + (int)((GIntBig)myrand_r(&seed) * nMaxYWin / MYRAND_MAX);
271 22400 : if (nXOff + nXWin > nXSize)
272 1200 : nXWin = nXSize - nXOff;
273 22400 : if (nYOff + nYWin > nYSize)
274 1184 : nYWin = nYSize - nYOff;
275 22400 : AddRequest(psRequestList, psRequestLast, nXOff, nYOff, nXWin, nYWin,
276 : nQueriedBands);
277 : }
278 24 : return nQueriedBands * nMaxXWin * nMaxYWin;
279 : }
280 :
281 0 : static int CreateLineStrategyRequests(GDALDataset *poDS, int nMaxRequests,
282 : Request *&psRequestList,
283 : Request *&psRequestLast)
284 : {
285 0 : int nXSize = poDS->GetRasterXSize();
286 0 : int nYSize = poDS->GetRasterYSize();
287 0 : int nQueriedBands = MIN(4, poDS->GetRasterCount());
288 0 : int bStop = FALSE;
289 0 : int nRequests = 0;
290 0 : for (int iLoop = 0; !bStop && iLoop < nLoops; iLoop++)
291 : {
292 0 : for (int nYOff = 0; nYOff < nYSize; nYOff++)
293 : {
294 0 : if (nMaxRequests > 0 && nRequests == nMaxRequests)
295 : {
296 0 : bStop = TRUE;
297 0 : break;
298 : }
299 0 : AddRequest(psRequestList, psRequestLast, 0, nYOff, nXSize, 1,
300 : nQueriedBands);
301 0 : nRequests++;
302 : }
303 : }
304 0 : return nQueriedBands * nXSize;
305 : }
306 :
307 0 : static int CreateBlockStrategyRequests(GDALDataset *poDS, int nMaxRequests,
308 : Request *&psRequestList,
309 : Request *&psRequestLast)
310 : {
311 0 : int nXSize = poDS->GetRasterXSize();
312 0 : int nYSize = poDS->GetRasterYSize();
313 0 : int nMaxXWin = MIN(1000, nXSize / 10 + 1);
314 0 : int nMaxYWin = MIN(1000, nYSize / 10 + 1);
315 0 : int nQueriedBands = MIN(4, poDS->GetRasterCount());
316 0 : int bStop = FALSE;
317 0 : int nRequests = 0;
318 0 : for (int iLoop = 0; !bStop && iLoop < nLoops; iLoop++)
319 : {
320 0 : for (int nYOff = 0; !bStop && nYOff < nYSize; nYOff += nMaxYWin)
321 : {
322 0 : int nReqYSize =
323 0 : (nYOff + nMaxYWin > nYSize) ? nYSize - nYOff : nMaxYWin;
324 0 : for (int nXOff = 0; nXOff < nXSize; nXOff += nMaxXWin)
325 : {
326 0 : if (nMaxRequests > 0 && nRequests == nMaxRequests)
327 : {
328 0 : bStop = TRUE;
329 0 : break;
330 : }
331 0 : int nReqXSize =
332 0 : (nXOff + nMaxXWin > nXSize) ? nXSize - nXOff : nMaxXWin;
333 0 : AddRequest(psRequestList, psRequestLast, nXOff, nYOff,
334 : nReqXSize, nReqYSize, nQueriedBands);
335 0 : nRequests++;
336 : }
337 : }
338 : }
339 0 : return nQueriedBands * nMaxXWin * nMaxYWin;
340 : }
341 :
342 24 : TEST(testblockcache, test)
343 : {
344 : int i;
345 6 : int nThreads = CPLGetNumCPUs();
346 12 : std::vector<CPLJoinableThread *> apsThreads;
347 6 : Strategy eStrategy = STRATEGY_RANDOM;
348 6 : int bNewDatasetOption = FALSE;
349 6 : int nXSize = 5000;
350 6 : int nYSize = 5000;
351 6 : int nBands = 4;
352 6 : char **papszOptions = nullptr;
353 6 : int bOnDisk = FALSE;
354 12 : std::vector<ThreadDescription> asThreadDescription;
355 6 : int bMemDriver = FALSE;
356 6 : GDALDataset *poMEMDS = nullptr;
357 6 : int bMigrate = FALSE;
358 6 : int nMaxRequests = -1;
359 :
360 6 : GDALAllRegister();
361 :
362 6 : int argc = global_argc;
363 6 : char **argv = global_argv;
364 23 : for (i = 1; i < argc; i++)
365 : {
366 17 : if (EQUAL(argv[i], "-threads") && i + 1 < argc)
367 : {
368 0 : i++;
369 0 : nThreads = atoi(argv[i]);
370 : }
371 17 : else if (EQUAL(argv[i], "-loops") && i + 1 < argc)
372 : {
373 4 : i++;
374 4 : nLoops = atoi(argv[i]);
375 4 : if (nLoops <= 0)
376 0 : nLoops = INT_MAX;
377 : }
378 13 : else if (EQUAL(argv[i], "-max_requests") && i + 1 < argc)
379 : {
380 0 : i++;
381 0 : nMaxRequests = atoi(argv[i]);
382 : }
383 13 : else if (EQUAL(argv[i], "-strategy") && i + 1 < argc)
384 : {
385 0 : i++;
386 0 : if (EQUAL(argv[i], "random"))
387 0 : eStrategy = STRATEGY_RANDOM;
388 0 : else if (EQUAL(argv[i], "line"))
389 0 : eStrategy = STRATEGY_LINE;
390 0 : else if (EQUAL(argv[i], "block"))
391 0 : eStrategy = STRATEGY_BLOCK;
392 : else
393 0 : Usage();
394 : }
395 13 : else if (EQUAL(argv[i], "-xsize") && i + 1 < argc)
396 : {
397 0 : i++;
398 0 : nXSize = atoi(argv[i]);
399 0 : bNewDatasetOption = TRUE;
400 : }
401 13 : else if (EQUAL(argv[i], "-ysize") && i + 1 < argc)
402 : {
403 0 : i++;
404 0 : nYSize = atoi(argv[i]);
405 0 : bNewDatasetOption = TRUE;
406 : }
407 13 : else if (EQUAL(argv[i], "-bands") && i + 1 < argc)
408 : {
409 0 : i++;
410 0 : nBands = atoi(argv[i]);
411 0 : bNewDatasetOption = TRUE;
412 : }
413 13 : else if (EQUAL(argv[i], "-co") && i + 1 < argc)
414 : {
415 5 : i++;
416 5 : papszOptions = CSLAddString(papszOptions, argv[i]);
417 5 : bNewDatasetOption = TRUE;
418 : }
419 8 : else if (EQUAL(argv[i], "-ondisk"))
420 : {
421 0 : bOnDisk = TRUE;
422 0 : bNewDatasetOption = TRUE;
423 : }
424 8 : else if (EQUAL(argv[i], "-check"))
425 : {
426 6 : bCheck = TRUE;
427 6 : bNewDatasetOption = TRUE;
428 : }
429 2 : else if (EQUAL(argv[i], "-memdriver"))
430 : {
431 1 : bMemDriver = TRUE;
432 1 : bNewDatasetOption = TRUE;
433 : }
434 1 : else if (EQUAL(argv[i], "-migrate"))
435 1 : bMigrate = TRUE;
436 0 : else if (argv[i][0] == '-')
437 0 : Usage();
438 0 : else if (pszDataset == nullptr)
439 0 : pszDataset = argv[i];
440 : else
441 : {
442 0 : Usage();
443 : }
444 : }
445 :
446 6 : if (pszDataset != nullptr && bNewDatasetOption)
447 0 : Usage();
448 :
449 6 : CPLDebug("TEST", "Using %d threads", nThreads);
450 :
451 6 : int bCreatedDataset = FALSE;
452 6 : if (pszDataset == nullptr)
453 : {
454 6 : bCreatedDataset = TRUE;
455 6 : if (bOnDisk)
456 0 : pszDataset = "/tmp/tmp.tif";
457 : else
458 6 : pszDataset = "/vsimem/tmp.tif";
459 : GDALDataset *poDS =
460 6 : ((GDALDriver *)GDALGetDriverByName((bMemDriver) ? "MEM" : "GTiff"))
461 6 : ->Create(pszDataset, nXSize, nYSize, nBands, GDT_Byte,
462 : papszOptions);
463 6 : if (bCheck)
464 : {
465 : GByte *pabyLine =
466 6 : (GByte *)VSIMalloc(static_cast<size_t>(nBands) * nXSize);
467 30006 : for (int iY = 0; iY < nYSize; iY++)
468 : {
469 150030000 : for (int iX = 0; iX < nXSize; iX++)
470 : {
471 750000000 : for (int iBand = 0; iBand < nBands; iBand++)
472 : {
473 600000000 : unsigned long seed =
474 600000000 : iBand * nXSize * nYSize + iY * nXSize + iX;
475 600000000 : pabyLine[iBand * nXSize + iX] =
476 600000000 : (GByte)(myrand_r(&seed) & 0xff);
477 : }
478 : }
479 30000 : CPL_IGNORE_RET_VAL(poDS->RasterIO(GF_Write, 0, iY, nXSize, 1,
480 : pabyLine, nXSize, 1, GDT_Byte,
481 : nBands, nullptr, 0, 0, 0
482 : #ifdef GDAL_COMPILATION
483 : ,
484 : nullptr
485 : #endif
486 : ));
487 : }
488 6 : VSIFree(pabyLine);
489 : }
490 6 : if (bMemDriver)
491 1 : poMEMDS = poDS;
492 : else
493 5 : GDALClose(poDS);
494 : }
495 : else
496 : {
497 0 : bCheck = FALSE;
498 : }
499 6 : CSLDestroy(papszOptions);
500 6 : papszOptions = nullptr;
501 :
502 6 : Request *psGlobalRequestLast = nullptr;
503 :
504 30 : for (i = 0; i < nThreads; i++)
505 : {
506 : GDALDataset *poDS;
507 : // Since GDAL 2.0, the MEM driver is thread-safe, i.e. does not use the
508 : // block cache, but only for operations not involving resampling, which
509 : // is the case here
510 24 : if (poMEMDS)
511 4 : poDS = poMEMDS;
512 : else
513 : {
514 20 : poDS = (GDALDataset *)GDALOpen(pszDataset, GA_ReadOnly);
515 20 : if (poDS == nullptr)
516 0 : exit(1);
517 : }
518 24 : if (bMigrate)
519 : {
520 4 : Resource *psResource = (Resource *)CPLMalloc(sizeof(Resource));
521 4 : psResource->poDS = poDS;
522 : int nBufferSize;
523 4 : if (eStrategy == STRATEGY_RANDOM)
524 4 : nBufferSize = CreateRandomStrategyRequests(poDS, nMaxRequests,
525 : psGlobalRequestList,
526 : psGlobalRequestLast);
527 0 : else if (eStrategy == STRATEGY_LINE)
528 0 : nBufferSize = CreateLineStrategyRequests(poDS, nMaxRequests,
529 : psGlobalRequestList,
530 : psGlobalRequestLast);
531 : else
532 0 : nBufferSize = CreateBlockStrategyRequests(poDS, nMaxRequests,
533 : psGlobalRequestList,
534 : psGlobalRequestLast);
535 4 : psResource->pBuffer = CPLMalloc(nBufferSize);
536 4 : PutResourceAtEnd(psResource);
537 : }
538 : else
539 : {
540 : ThreadDescription sThreadDescription;
541 20 : sThreadDescription.poDS = poDS;
542 20 : sThreadDescription.psRequestList = nullptr;
543 20 : Request *psRequestLast = nullptr;
544 20 : if (eStrategy == STRATEGY_RANDOM)
545 20 : sThreadDescription.nBufferSize = CreateRandomStrategyRequests(
546 : poDS, nMaxRequests, sThreadDescription.psRequestList,
547 : psRequestLast);
548 0 : else if (eStrategy == STRATEGY_LINE)
549 0 : sThreadDescription.nBufferSize = CreateLineStrategyRequests(
550 : poDS, nMaxRequests, sThreadDescription.psRequestList,
551 : psRequestLast);
552 : else
553 0 : sThreadDescription.nBufferSize = CreateBlockStrategyRequests(
554 : poDS, nMaxRequests, sThreadDescription.psRequestList,
555 : psRequestLast);
556 20 : asThreadDescription.push_back(sThreadDescription);
557 : }
558 : }
559 :
560 6 : if (bCreatedDataset && poMEMDS == nullptr && bOnDisk)
561 : {
562 0 : CPLPushErrorHandler(CPLQuietErrorHandler);
563 0 : VSIUnlink(pszDataset);
564 0 : CPLPopErrorHandler();
565 : }
566 :
567 6 : if (bMigrate)
568 : {
569 1 : psLock = CPLCreateLock(LOCK_SPIN);
570 : }
571 :
572 30 : for (i = 0; i < nThreads; i++)
573 : {
574 : CPLJoinableThread *pThread;
575 24 : if (bMigrate)
576 4 : pThread = CPLCreateJoinableThread(ThreadFuncWithMigration, nullptr);
577 : else
578 20 : pThread = CPLCreateJoinableThread(ThreadFuncDedicatedDataset,
579 20 : &(asThreadDescription[i]));
580 24 : apsThreads.push_back(pThread);
581 : }
582 30 : for (i = 0; i < nThreads; i++)
583 : {
584 24 : CPLJoinThread(apsThreads[i]);
585 24 : if (!bMigrate && poMEMDS == nullptr)
586 16 : GDALClose(asThreadDescription[i].poDS);
587 : }
588 10 : while (psGlobalResourceList != nullptr)
589 : {
590 4 : CPLFree(psGlobalResourceList->pBuffer);
591 4 : if (poMEMDS == nullptr)
592 4 : GDALClose(psGlobalResourceList->poDS);
593 4 : Resource *psNext = psGlobalResourceList->psNext;
594 4 : CPLFree(psGlobalResourceList);
595 4 : psGlobalResourceList = psNext;
596 : }
597 :
598 6 : if (psLock)
599 : {
600 1 : CPLDestroyLock(psLock);
601 : }
602 :
603 6 : if (bCreatedDataset && poMEMDS == nullptr)
604 : {
605 5 : CPLPushErrorHandler(CPLQuietErrorHandler);
606 5 : VSIUnlink(pszDataset);
607 5 : CPLPopErrorHandler();
608 : }
609 6 : if (poMEMDS)
610 1 : GDALClose(poMEMDS);
611 :
612 6 : EXPECT_EQ(GDALGetCacheUsed64(), 0);
613 :
614 6 : GDALDestroyDriverManager();
615 6 : }
616 :
617 : } // namespace
|