All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.swirlds.common.wiring.model.TraceableWiringModel Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 0.56.6
Show newest version
/*
 * 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;

import static com.swirlds.common.wiring.model.internal.analysis.ModelVertexMetaType.SCHEDULER;
import static com.swirlds.common.wiring.schedulers.builders.TaskSchedulerType.DIRECT;
import static com.swirlds.common.wiring.schedulers.builders.TaskSchedulerType.DIRECT_THREADSAFE;

import com.swirlds.common.wiring.model.diagram.ModelEdgeSubstitution;
import com.swirlds.common.wiring.model.diagram.ModelGroup;
import com.swirlds.common.wiring.model.diagram.ModelManualLink;
import com.swirlds.common.wiring.model.internal.analysis.CycleFinder;
import com.swirlds.common.wiring.model.internal.analysis.DirectSchedulerChecks;
import com.swirlds.common.wiring.model.internal.analysis.InputWireChecks;
import com.swirlds.common.wiring.model.internal.analysis.InputWireDescriptor;
import com.swirlds.common.wiring.model.internal.analysis.ModelEdge;
import com.swirlds.common.wiring.model.internal.analysis.ModelVertex;
import com.swirlds.common.wiring.model.internal.analysis.StandardVertex;
import com.swirlds.common.wiring.model.internal.analysis.WiringFlowchart;
import com.swirlds.common.wiring.schedulers.TaskScheduler;
import com.swirlds.common.wiring.schedulers.builders.TaskSchedulerType;
import com.swirlds.common.wiring.wires.SolderType;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Common functionality for wiring model implementations. Has methods for registering information about the topology of
 * wiring that is appropriate for internal use by the framework, but should not be exposed to the end users of the
 * wiring framework.
 */
public abstract class TraceableWiringModel implements WiringModel {

    /**
     * A map of vertex names to vertices.
     */
    private final Map vertices = new HashMap<>();

    /**
     * A set of all edges in the model.
     */
    private final Set edges = new HashSet<>();

    /**
     * Input wires that have been created.
     */
    private final Set inputWires = new HashSet<>();

    /**
     * Input wires that have been bound to a handler.
     */
    private final Set boundInputWires = new HashSet<>();

    /**
     * Input wires with at least one thing soldered to them.
     */
    private final Set solderedInputWires = new HashSet<>();

    /**
     * All task schedulers in the model.
     */
    protected final List> schedulers = new ArrayList<>();

    /**
     * True if start() has been called.
     */
    private boolean started = false;

    /**
     * True if backpressure is enabled.
     */
    private final boolean backpressureEnabled;

    /**
     * Constructor.
     *
     * @param backpressureEnabled true if backpressure is enabled
     */
    TraceableWiringModel(final boolean backpressureEnabled) {
        this.backpressureEnabled = backpressureEnabled;
    }

    /**
     * If true then backpressure is enabled. If false then this model will never apply backpressure internally.
     *
     * @return true if backpressure is enabled for this model
     */
    public boolean isBackpressureEnabled() {
        return backpressureEnabled;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean checkForCyclicalBackpressure() {
        return CycleFinder.checkForCyclicalBackPressure(vertices.values());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean checkForIllegalDirectSchedulerUsage() {
        return DirectSchedulerChecks.checkForIllegalDirectSchedulerUse(vertices.values());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean checkForUnboundInputWires() {
        return InputWireChecks.checkForUnboundInputWires(inputWires, boundInputWires);
    }

    /**
     * {@inheritDoc}
     */
    @NonNull
    @Override
    public String generateWiringDiagram(
            @NonNull final List groups,
            @NonNull final List substitutions,
            @NonNull final List manualLinks,
            final boolean moreMystery) {
        addVertexForUnsolderedInputWires(moreMystery);
        final WiringFlowchart flowchart = new WiringFlowchart(vertices, substitutions, groups, manualLinks);
        return flowchart.render();
    }

    /**
     * Add a special vertex for all unsoldered input wires.
     */
    private void addVertexForUnsolderedInputWires(final boolean moreMystery) {
        final Set unsolderedInputWires = new HashSet<>(inputWires);
        unsolderedInputWires.removeAll(solderedInputWires);

        if (unsolderedInputWires.isEmpty()) {
            return;
        }

        final ModelVertex unsolderedDataSource =
                new StandardVertex("Mystery Input", DIRECT_THREADSAFE, SCHEDULER, null, true);
        vertices.put(unsolderedDataSource.getName(), unsolderedDataSource);

        for (final InputWireDescriptor unsolderedInputWire : unsolderedInputWires) {
            final ModelVertex destination = getVertex(unsolderedInputWire.taskSchedulerName());

            final String edgeDescription = moreMystery ? "mystery data" : unsolderedInputWire.name();
            final ModelEdge edge = new ModelEdge(unsolderedDataSource, destination, edgeDescription, true, true);
            unsolderedDataSource.getOutgoingEdges().add(edge);
        }
    }

    /**
     * Register a task scheduler with the wiring model.
     *
     * @param scheduler the task scheduler to register
     * @param hyperlink the hyperlink to the documentation for this vertex, or null if there is no documentation
     */
    public void registerScheduler(@NonNull final TaskScheduler scheduler, @Nullable final String hyperlink) {
        throwIfStarted();
        Objects.requireNonNull(scheduler);
        schedulers.add(scheduler);
        registerVertex(scheduler.getName(), scheduler.getType(), hyperlink, scheduler.isInsertionBlocking());
    }

    /**
     * Register a vertex in the wiring model. These are either task schedulers or wire transformers.
     *
     * @param vertexName          the name of the vertex
     * @param type                the type of task scheduler that corresponds to this vertex.
     * @param hyperlink           the hyperlink to the documentation for this vertex, or null if there is no
     *                            documentation
     * @param insertionIsBlocking if true then insertion may block until capacity is available
     */
    public void registerVertex(
            @NonNull final String vertexName,
            @NonNull final TaskSchedulerType type,
            @Nullable final String hyperlink,
            final boolean insertionIsBlocking) {
        throwIfStarted();
        Objects.requireNonNull(vertexName);
        Objects.requireNonNull(type);
        final boolean unique = vertices.put(
                        vertexName, new StandardVertex(vertexName, type, SCHEDULER, hyperlink, insertionIsBlocking))
                == null;
        if (!unique) {
            throw new IllegalArgumentException("Duplicate vertex name: " + vertexName);
        }
    }

    /**
     * Register an edge between two vertices.
     *
     * @param originVertex      the origin vertex
     * @param destinationVertex the destination vertex
     * @param label             the label of the edge
     * @param solderType        the type of solder connection
     */
    public void registerEdge(
            @NonNull final String originVertex,
            @NonNull final String destinationVertex,
            @NonNull final String label,
            @NonNull final SolderType solderType) {
        throwIfStarted();

        final boolean blockingEdge = solderType == SolderType.PUT;

        final ModelVertex origin = getVertex(originVertex);
        final ModelVertex destination = getVertex(destinationVertex);
        final boolean blocking = blockingEdge && destination.isInsertionIsBlocking();

        final ModelEdge edge = new ModelEdge(origin, destination, label, blocking, false);
        origin.getOutgoingEdges().add(edge);

        final boolean unique = edges.add(edge);
        if (!unique) {
            throw new IllegalArgumentException(
                    "Duplicate edge: " + originVertex + " -> " + destinationVertex + ", label = " + label);
        }

        solderedInputWires.add(new InputWireDescriptor(destinationVertex, label));
    }

    /**
     * Register an input wire with the wiring model. For every input wire registered via this method, the model expects
     * to see exactly one registration via {@link #registerInputWireBinding(String, String)}.
     *
     * @param taskSchedulerName the name of the task scheduler that the input wire is associated with
     * @param inputWireName     the name of the input wire
     */
    public void registerInputWireCreation(
            @NonNull final String taskSchedulerName, @NonNull final String inputWireName) {
        throwIfStarted();

        final boolean unique = inputWires.add(new InputWireDescriptor(taskSchedulerName, inputWireName));
        if (!unique) {
            throw new IllegalStateException(
                    "Duplicate input wire " + inputWireName + " for scheduler " + taskSchedulerName);
        }
    }

    /**
     * Register an input wire binding with the wiring model. For every input wire registered via
     * {@link #registerInputWireCreation(String, String)}, the model expects to see exactly one registration via this
     * method.
     *
     * @param taskSchedulerName the name of the task scheduler that the input wire is associated with
     * @param inputWireName     the name of the input wire
     */
    public void registerInputWireBinding(@NonNull final String taskSchedulerName, @NonNull final String inputWireName) {
        throwIfStarted();

        final InputWireDescriptor descriptor = new InputWireDescriptor(taskSchedulerName, inputWireName);

        final boolean registered = inputWires.contains(descriptor);
        if (!registered) {
            throw new IllegalStateException(
                    "Input wire " + inputWireName + " for scheduler " + taskSchedulerName + " was not registered");
        }

        final boolean unique = boundInputWires.add(descriptor);
        if (!unique) {
            throw new IllegalStateException("Input wire " + inputWireName + " for scheduler " + taskSchedulerName
                    + " should not be bound more than once");
        }
    }

    /**
     * Throw an exception if start() has already been called.
     */
    protected void throwIfStarted() {
        if (started) {
            throw new IllegalStateException("start() has already been called, operation not permitted.");
        }
    }

    /**
     * Throw an exception if the wiring model has not been started.
     */
    protected void throwIfNotStarted() {
        if (!started) {
            throw new IllegalStateException("start() has not been called, operation not permitted.");
        }
    }

    /**
     * Mark the wiring model as started.
     */
    protected void markAsStarted() {
        started = true;
    }

    /**
     * Find an existing vertex
     *
     * @param vertexName the name of the vertex
     * @return the vertex
     */
    @NonNull
    private ModelVertex getVertex(@NonNull final String vertexName) {
        final ModelVertex vertex = vertices.get(vertexName);
        if (vertex != null) {
            return vertex;
        }

        // Create an ad hoc vertex.
        final StandardVertex adHocVertex = new StandardVertex(vertexName, DIRECT, SCHEDULER, null, true);

        vertices.put(vertexName, adHocVertex);
        return adHocVertex;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy