com.swirlds.common.wiring.model.internal.analysis.DirectSchedulerChecks Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swirlds-common Show documentation
Show all versions of swirlds-common Show documentation
Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.
/*
* Copyright (C) 2023-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.swirlds.common.wiring.model.internal.analysis;
import static com.swirlds.logging.legacy.LogMarker.EXCEPTION;
import static com.swirlds.logging.legacy.LogMarker.STARTUP;
import com.swirlds.common.wiring.schedulers.builders.TaskSchedulerType;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* A utility for checking direct scheduler use.
*/
public final class DirectSchedulerChecks {
private static final Logger logger = LogManager.getLogger(DirectSchedulerChecks.class);
private DirectSchedulerChecks() {}
/**
* Check for illegal direct scheduler use. Rules are as follows:
*
*
* -
* Calling into a component with type {@link TaskSchedulerType#DIRECT DIRECT}
* from a component with {@link TaskSchedulerType#CONCURRENT CONCURRENT} is not
* allowed.
*
* -
* Calling into a component with type {@link TaskSchedulerType#DIRECT DIRECT}
* from more than one component with type
* {@link TaskSchedulerType#SEQUENTIAL SEQUENTIAL} or type
* {@link TaskSchedulerType#SEQUENTIAL_THREAD SEQUENTIAL_THREAD} is not allowed.
*
* -
* Calling into a component A with type
* {@link TaskSchedulerType#DIRECT DIRECT} from component B with type
* {@link TaskSchedulerType#DIRECT DIRECT} or type
* {@link TaskSchedulerType#DIRECT_THREADSAFE DIRECT_THREADSAFE} counts as a call
* into B from all components calling into component A.
*
*
*
* @param vertices the vertices in the wiring model
* @return true if there is illegal direct scheduler use
*/
public static boolean checkForIllegalDirectSchedulerUse(@NonNull final Collection vertices) {
boolean illegalAccessDetected = false;
// Note: this is only logged if we detect a problem.
final StringBuilder sb = new StringBuilder("Illegal direct scheduler use detected:\n");
// A map from each direct vertex to a set of non-direct schedulers that call into it.
// If access is legal, then each of these sets should contain at most one element.
final Map> directVertexCallers = new HashMap<>();
for (final ModelVertex vertex : vertices) {
final TaskSchedulerType vertexType = vertex.getType();
if (vertexType == TaskSchedulerType.DIRECT || vertexType == TaskSchedulerType.DIRECT_THREADSAFE) {
// we can ignore direct schedulers at this phase. We care about calls INTO direct schedulers,
// not calls OUT OF direct schedulers.
continue;
}
final Set directSchedulersAccessed = collectDirectVerticesAccessedByScheduler(vertex);
if (vertexType == TaskSchedulerType.CONCURRENT && !directSchedulersAccessed.isEmpty()) {
// It is illegal for a concurrent scheduler to call into a direct scheduler.
illegalAccessDetected = true;
sb.append(" ")
.append(vertex.getName())
.append(" is a concurrent scheduler that calls into direct scheduler(s):\n");
for (final ModelVertex directScheduler : directSchedulersAccessed) {
sb.append(" - ").append(directScheduler.getName()).append("\n");
}
}
for (final ModelVertex directScheduler : directSchedulersAccessed) {
directVertexCallers
.computeIfAbsent(directScheduler, k -> new HashSet<>())
.add(vertex);
}
}
// Now, check to see if any direct schedulers are called into by more than one non-direct scheduler.
for (final Map.Entry> entry : directVertexCallers.entrySet()) {
final ModelVertex directScheduler = entry.getKey();
final Set callers = entry.getValue();
if (callers.size() > 1) {
illegalAccessDetected = true;
sb.append(" ")
.append(directScheduler.getName())
.append(" is called into by more than one non-direct scheduler:\n");
for (final ModelVertex caller : callers) {
sb.append(" - ").append(caller.getName()).append("\n");
}
}
}
if (illegalAccessDetected) {
logger.error(EXCEPTION.getMarker(), sb.toString());
} else {
logger.info(STARTUP.getMarker(), "No illegal direct scheduler use detected in the wiring model.");
}
return illegalAccessDetected;
}
/**
* Collect all direct vertices that are accessed by a scheduler.
*
* @param scheduler the scheduler to check
* @return the set of direct vertices accessed by the scheduler
*/
@NonNull
private static Set collectDirectVerticesAccessedByScheduler(@NonNull final ModelVertex scheduler) {
final Set directSchedulersAccessed = new HashSet<>();
final Deque stack = new LinkedList<>();
final Set visited = new HashSet<>();
stack.addLast(scheduler);
visited.add(scheduler);
while (!stack.isEmpty()) {
final ModelVertex next = stack.removeLast();
for (final ModelEdge edge : next.getOutgoingEdges()) {
final ModelVertex destination = edge.getDestination();
final TaskSchedulerType destinationType = destination.getType();
if (destinationType != TaskSchedulerType.DIRECT
&& destinationType != TaskSchedulerType.DIRECT_THREADSAFE) {
// we don't need to traverse edges that lead into non-direct schedulers
continue;
}
if (destinationType == TaskSchedulerType.DIRECT) {
directSchedulersAccessed.add(destination);
}
if (visited.add(destination)) {
stack.addLast(destination);
visited.add(destination);
}
}
}
return directSchedulersAccessed;
}
}