@@ -1282,44 +1282,69 @@ void CArchiveScanner::WriteCacheData(const std::string& filename)
12821282 allPoolRootDirs.emplace (CPoolArchive::GetPoolRootDirectory (ai.path + ai.origName ));
12831283 }
12841284
1285- const uint32_t seed = std::chrono::system_clock::now ().time_since_epoch ().count ();
1286- std::mt19937 generator (seed);
1287- std::uniform_int_distribution<size_t > distribution (0 , poolFilesInfo.size () - 1 );
1288- auto startOffset = distribution (generator);
1289-
1290- auto st = poolFilesInfo.begin ();
1291- std::advance (st, startOffset);
1292- auto it = st;
1293-
1294- // we only want to check if the file still exists, we don't check for size / modDate
1295- // this is checked in the checksum code anyway.
1296- const auto ExistenceTest = [&allPoolRootDirs = std::as_const (allPoolRootDirs)](const auto & it) {
1297- for (const auto & poolRootDir : allPoolRootDirs) {
1298- const auto fileName = CPoolArchive::GetPoolFilePath (poolRootDir, it->first );
1299- if (FileSystem::FileExists (fileName)) {
1300- return true ;
1285+ // No pool archives means we have no roots to verify file existence
1286+ // against. Without this guard, ExistenceTest below returns false for
1287+ // every entry, the loop erases the entire cache, and the next
1288+ // iteration dereferences begin()==end() on the now-empty map.
1289+ if (!allPoolRootDirs.empty ()) {
1290+ const uint32_t seed = std::chrono::system_clock::now ().time_since_epoch ().count ();
1291+ std::mt19937 generator (seed);
1292+ std::uniform_int_distribution<size_t > distribution (0 , poolFilesInfo.size () - 1 );
1293+ auto startOffset = distribution (generator);
1294+
1295+ auto st = poolFilesInfo.begin ();
1296+ std::advance (st, startOffset);
1297+ auto it = st;
1298+
1299+ // we only want to check if the file still exists, we don't check for size / modDate
1300+ // this is checked in the checksum code anyway.
1301+ const auto ExistenceTest = [&allPoolRootDirs = std::as_const (allPoolRootDirs)](const auto & it) {
1302+ for (const auto & poolRootDir : allPoolRootDirs) {
1303+ const auto fileName = CPoolArchive::GetPoolFilePath (poolRootDir, it->first );
1304+ if (FileSystem::FileExists (fileName)) {
1305+ return true ;
1306+ }
1307+ }
1308+ return false ;
1309+ };
1310+
1311+ // can't spend too much time in this code, thus set the deadline and rely on
1312+ // random luck and sheer amount of invocations to eventually remove most if not all
1313+ // stale items from the pool cache
1314+ static constexpr int64_t MAX_POOL_VERIFICATION_TIME = 1 * 1000 ;
1315+ for (auto t0 = spring_now (), t1 = t0; (t1 - t0).toMilliSecsi () < MAX_POOL_VERIFICATION_TIME ; t1 = spring_now ()) {
1316+ // Map can drain to empty mid-loop (e.g. all entries genuinely
1317+ // missing). begin() then equals end(); ExistenceTest(end())
1318+ // would dereference past the allocated buckets array.
1319+ if (it == poolFilesInfo.end ())
1320+ break ;
1321+
1322+ // cleanup files that got deleted in the meantime
1323+ if (ExistenceTest (it)) {
1324+ ++it;
1325+ } else {
1326+ // erase invalidates the erased iterator. If we're erasing
1327+ // the loop's anchor, re-anchor to the successor so the
1328+ // `it == st` termination check below still works; otherwise
1329+ // st dangles and we'd burn the full deadline re-checking
1330+ // survivors.
1331+ const bool erasingStart = (it == st);
1332+ it = poolFilesInfo.erase (it);
1333+ if (erasingStart)
1334+ st = it;
13011335 }
1302- }
1303- return false ;
1304- };
1305-
1306- // can't spend too much time in this code, thus set the deadline and rely on
1307- // random luck and sheer amount of invocations to eventually remove most if not all
1308- // stale items from the pool cache
1309- static constexpr int64_t MAX_POOL_VERIFICATION_TIME = 1 * 1000 ;
1310- for (auto t0 = spring_now (), t1 = t0; (t1 - t0).toMilliSecsi () < MAX_POOL_VERIFICATION_TIME ; t1 = spring_now ()) {
1311- // cleanup files that got deleted in the meantime
13121336
1313- if (ExistenceTest (it))
1314- ++it;
1315- else
1316- it = poolFilesInfo.erase (it);
1337+ if (it == poolFilesInfo.end ())
1338+ it = poolFilesInfo.begin (); // rewind to the very start
13171339
1318- if (it == poolFilesInfo.end ())
1319- it = poolFilesInfo.begin (); // rewind to the very start
1340+ // st may have been re-anchored to end() above; wrap it too so
1341+ // the comparison is meaningful.
1342+ if (st == poolFilesInfo.end ())
1343+ st = poolFilesInfo.begin ();
13201344
1321- if (it == st)
1322- break ; // everything got checked and we're back to the starting iterator
1345+ if (it == st)
1346+ break ; // everything got checked and we're back to the starting iterator
1347+ }
13231348 }
13241349 }
13251350
0 commit comments