BlockPoolSliceScanner#scan contains a "while" loop that continues to verify (i.e. scan) blocks until the blockInfoSet is empty (or some other conditions like a timeout have occurred.) In order to do this, it calls BlockPoolSliceScanner#verifyFirstBlock. This is intended to grab the first block in the blockInfoSet, verify it, and remove it from that set. (blockInfoSet is sorted by last scan time.) Unfortunately, if we hit a certain bug in updateScanStatus, the block may never be removed from blockInfoSet. When this happens, we keep rescanning the exact same block until the timeout hits.
The bug is triggered when a block winds up in blockInfoSet but not in blockMap. You can see it clearly in this code:
If info == null, we never call delBlockInfo, the function which is intended to remove the blockInfoSet entry.
Luckily, there is a simple fix here... the variable that updateScanStatus is being passed is actually a BlockInfo object, so we can simply call delBlockInfo on it directly, without doing a lookup in the blockMap. This is both faster and more robust.