Skip to content

Commit 382c284

Browse files
authored
fix: 22020: One of virtual hashing threads is always blocked waiting for the root hashing task to complete (#22022)
Fixes: #22020 Reviewed-by: Ivan Malygin <[email protected]>, Nikita Lebedev <[email protected]> Signed-off-by: Artem Ananev <[email protected]>
1 parent 3d4becd commit 382c284

File tree

1 file changed

+28
-41
lines changed
  • platform-sdk/swirlds-virtualmap/src/main/java/com/swirlds/virtualmap/internal/hash

1 file changed

+28
-41
lines changed

platform-sdk/swirlds-virtualmap/src/main/java/com/swirlds/virtualmap/internal/hash/VirtualHasher.java

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -386,32 +386,45 @@ public Hash hash(
386386
final @NonNull VirtualMapConfig virtualMapConfig) {
387387
requireNonNull(virtualMapConfig);
388388

389+
this.hashReader = hashReader;
389390
// We don't want to include null checks everywhere, so let the listener be NoopListener if null
390-
final VirtualHashListener normalizedListener = listener == null
391+
this.listener = listener == null
391392
? new VirtualHashListener() {
392393
/* noop */
393394
}
394395
: listener;
396+
// Let the listener know we have started hashing.
397+
this.listener.onHashingStarted(firstLeafPath, lastLeafPath);
395398

396399
final ForkJoinPool pool = Thread.currentThread() instanceof ForkJoinWorkerThread thread
397400
? thread.getPool()
398401
: getHashingPool(virtualMapConfig);
399402

400-
return pool.invoke(ForkJoinTask.adapt(() -> hashInternal(
401-
hashReader,
402-
sortedDirtyLeaves,
403-
firstLeafPath,
404-
lastLeafPath,
405-
normalizedListener,
406-
virtualMapConfig,
407-
pool)));
403+
final ChunkHashTask rootTask = pool.invoke(ForkJoinTask.adapt(
404+
() -> hashInternal(sortedDirtyLeaves, firstLeafPath, lastLeafPath, virtualMapConfig, pool)));
405+
if (rootTask != null) {
406+
try {
407+
rootTask.join();
408+
} catch (final Exception e) {
409+
if (!shutdown.get()) {
410+
logger.error(EXCEPTION.getMarker(), "Failed to wait for all hashing tasks", e);
411+
throw e;
412+
}
413+
}
414+
}
415+
416+
this.listener.onHashingCompleted();
417+
418+
this.hashReader = null;
419+
this.listener = null;
420+
421+
return rootTask != null ? rootTask.getResult() : null;
408422
}
409423

410424
/**
411-
* Internal method calculating the hash of the tree in a given fork-join pool.
425+
* Internal method calculating the hash of the tree in a given fork-join pool. This method
426+
* returns a root hashing task, which can be used to wait till hashing process is complete.
412427
*
413-
* @param hashReader
414-
* Return a {@link Hash} by path. Used when this method needs to look up clean nodes.
415428
* @param sortedDirtyLeaves
416429
* A stream of dirty leaves sorted in <strong>ASCENDING PATH ORDER</strong>, such that path
417430
* 1234 comes before 1235. If null or empty, a null hash result is returned.
@@ -421,36 +434,25 @@ public Hash hash(
421434
* @param lastLeafPath
422435
* The lastLeafPath of the tree that is being hashed. If &lt; 1, then a null hash result is returned.
423436
* No leaf in {@code sortedDirtyLeaves} may have a path greater than {@code lastLeafPath}.
424-
* @param listener
425-
* Hash listener. May be {@code null}
426437
* @param virtualMapConfig platform configuration for VirtualMap
427438
* @param pool the pool to use for hashing tasks.
428-
* @return calculated root hash, or null if there are no dirty leaves to hash.
439+
* @return the root hashing task, or null if there are no dirty leaves to hash.
429440
*/
430-
private Hash hashInternal(
431-
final @NonNull LongFunction<Hash> hashReader,
441+
private ChunkHashTask hashInternal(
432442
final @NonNull Iterator<VirtualLeafBytes> sortedDirtyLeaves,
433443
final long firstLeafPath,
434444
final long lastLeafPath,
435-
final @NonNull VirtualHashListener listener,
436445
final @NonNull VirtualMapConfig virtualMapConfig,
437446
final @NonNull ForkJoinPool pool) {
438-
// Let the listener know we have started hashing.
439-
listener.onHashingStarted(firstLeafPath, lastLeafPath);
440-
441447
if (!sortedDirtyLeaves.hasNext()) {
442448
// Nothing to hash.
443-
listener.onHashingCompleted();
444449
return null;
445450
} else {
446451
if ((firstLeafPath < 1) || (lastLeafPath < 1)) {
447452
throw new IllegalArgumentException("Dirty leaves stream is not empty, but leaf path range is empty");
448453
}
449454
}
450455

451-
this.hashReader = hashReader;
452-
this.listener = listener;
453-
454456
// Algo v6. This version is task based, where every task is responsible for hashing a small
455457
// chunk of the tree. Tasks are running in a fork-join pool, which is shared across all
456458
// virtual maps.
@@ -627,22 +629,7 @@ private Hash hashInternal(
627629
allTasks.forEach((path, task) -> task.complete());
628630
allTasks.clear();
629631

630-
try {
631-
rootTask.join();
632-
} catch (final Exception e) {
633-
if (shutdown.get()) {
634-
return null;
635-
}
636-
logger.error(EXCEPTION.getMarker(), "Failed to wait for all hashing tasks", e);
637-
throw e;
638-
}
639-
640-
listener.onHashingCompleted();
641-
642-
this.hashReader = null;
643-
this.listener = null;
644-
645-
return rootTask.getResult();
632+
return rootTask;
646633
}
647634

648635
public Hash emptyRootHash() {

0 commit comments

Comments
 (0)