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

io.aeron.driver.Receiver Maven / Gradle / Ivy

There is a newer version: 1.48.0
Show newest version
/*
 * Copyright 2014-2024 Real Logic Limited.
 *
 * 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
 *
 * https://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 io.aeron.driver;

import io.aeron.driver.media.DataTransportPoller;
import io.aeron.driver.media.ReceiveChannelEndpoint;
import io.aeron.driver.media.ReceiveDestinationTransport;
import io.aeron.driver.media.UdpChannel;
import io.aeron.driver.status.DutyCycleStallTracker;
import org.agrona.collections.ArrayListUtil;
import org.agrona.collections.ArrayUtil;
import org.agrona.concurrent.Agent;
import org.agrona.concurrent.CachedNanoClock;
import org.agrona.concurrent.NanoClock;
import org.agrona.concurrent.OneToOneConcurrentArrayQueue;
import org.agrona.concurrent.status.AtomicCounter;

import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.util.ArrayList;

import static io.aeron.driver.Configuration.PENDING_SETUPS_TIMEOUT_NS;
import static io.aeron.driver.status.SystemCounterDescriptor.BYTES_RECEIVED;
import static io.aeron.driver.status.SystemCounterDescriptor.RESOLUTION_CHANGES;

/**
 * Agent that receives messages streams and rebuilds {@link PublicationImage}s, plus iterates over them sending status
 * and control messages back to the {@link Sender}.
 */
public final class Receiver implements Agent
{
    private static final PublicationImage[] EMPTY_IMAGES = new PublicationImage[0];

    private final long reResolutionCheckIntervalNs;
    private long reResolutionDeadlineNs;
    private final DataTransportPoller dataTransportPoller;
    private final OneToOneConcurrentArrayQueue commandQueue;
    private final AtomicCounter totalBytesReceived;
    private final AtomicCounter resolutionChanges;
    private final NanoClock nanoClock;
    private final CachedNanoClock cachedNanoClock;
    private PublicationImage[] publicationImages = EMPTY_IMAGES;
    private final ArrayList pendingSetupMessages = new ArrayList<>();
    private final DriverConductorProxy conductorProxy;
    private final DutyCycleTracker dutyCycleTracker;

    Receiver(final MediaDriver.Context ctx)
    {
        dataTransportPoller = ctx.dataTransportPoller();
        commandQueue = ctx.receiverCommandQueue();
        totalBytesReceived = ctx.systemCounters().get(BYTES_RECEIVED);
        resolutionChanges = ctx.systemCounters().get(RESOLUTION_CHANGES);
        nanoClock = ctx.nanoClock();
        cachedNanoClock = ctx.receiverCachedNanoClock();
        conductorProxy = ctx.driverConductorProxy();
        reResolutionCheckIntervalNs = ctx.reResolutionCheckIntervalNs();
        dutyCycleTracker = ctx.receiverDutyCycleTracker();
    }

    /**
     * {@inheritDoc}
     */
    public void onStart()
    {
        final long nowNs = nanoClock.nanoTime();
        cachedNanoClock.update(nowNs);
        dutyCycleTracker.update(nowNs);
        reResolutionDeadlineNs = nowNs + reResolutionCheckIntervalNs;

        if (dutyCycleTracker instanceof DutyCycleStallTracker)
        {
            final DutyCycleStallTracker dutyCycleStallTracker = (DutyCycleStallTracker)dutyCycleTracker;

            dutyCycleStallTracker.maxCycleTime().appendToLabel(": " + conductorProxy.threadingMode().name());
            dutyCycleStallTracker.cycleTimeThresholdExceededCount().appendToLabel(
                ": threshold=" + dutyCycleStallTracker.cycleTimeThresholdNs() + "ns " +
                conductorProxy.threadingMode().name());
        }
    }

    /**
     * {@inheritDoc}
     */
    public void onClose()
    {
        dataTransportPoller.close();
    }

    /**
     * {@inheritDoc}
     */
    public String roleName()
    {
        return "receiver";
    }

    /**
     * {@inheritDoc}
     */
    public int doWork()
    {
        final long nowNs = nanoClock.nanoTime();
        cachedNanoClock.update(nowNs);
        dutyCycleTracker.measureAndUpdate(nowNs);

        int workCount = commandQueue.drain(CommandProxy.RUN_TASK, Configuration.COMMAND_DRAIN_LIMIT);

        final int bytesReceived = dataTransportPoller.pollTransports();
        totalBytesReceived.getAndAddOrdered(bytesReceived);

        final PublicationImage[] publicationImages = this.publicationImages;
        for (int lastIndex = publicationImages.length - 1, i = lastIndex; i >= 0; i--)
        {
            final PublicationImage image = publicationImages[i];
            if (image.isConnected(nowNs))
            {
                image.checkEosForDrainTransition(nowNs);

                workCount += image.sendPendingStatusMessage(nowNs);
                workCount += image.processPendingLoss();
                workCount += image.initiateAnyRttMeasurements(nowNs);
            }
            else
            {
                this.publicationImages = 1 == this.publicationImages.length ?
                    EMPTY_IMAGES : ArrayUtil.remove(this.publicationImages, i);
                image.removeFromDispatcher();
                image.receiverRelease();
            }
        }

        checkPendingSetupMessages(nowNs);

        if (reResolutionCheckIntervalNs > 0 && (reResolutionDeadlineNs - nowNs) < 0)
        {
            reResolutionDeadlineNs = nowNs + reResolutionCheckIntervalNs;
            dataTransportPoller.checkForReResolutions(nowNs, conductorProxy);
        }

        return workCount + bytesReceived;
    }

    void addPendingSetupMessage(
        final int sessionId,
        final int streamId,
        final int transportIndex,
        final ReceiveChannelEndpoint channelEndpoint,
        final boolean periodic,
        final InetSocketAddress controlAddress)
    {
        final PendingSetupMessageFromSource cmd = new PendingSetupMessageFromSource(
            sessionId, streamId, transportIndex, channelEndpoint, periodic, controlAddress);

        cmd.timeOfStatusMessageNs(cachedNanoClock.nanoTime());
        pendingSetupMessages.add(cmd);
    }

    void onAddSubscription(final ReceiveChannelEndpoint channelEndpoint, final int streamId)
    {
        channelEndpoint.dispatcher().addSubscription(streamId);
    }

    void onAddSubscription(final ReceiveChannelEndpoint channelEndpoint, final int streamId, final int sessionId)
    {
        channelEndpoint.dispatcher().addSubscription(streamId, sessionId);
        if (channelEndpoint.hasExplicitControl())
        {
            channelEndpoint.sendSetupElicitingStatusMessage(
                0, channelEndpoint.explicitControlAddress(), sessionId, streamId);
        }
    }

    void onRequestSetup(final ReceiveChannelEndpoint channelEndpoint, final int streamId, final int sessionId)
    {
        if (channelEndpoint.hasExplicitControl())
        {
            channelEndpoint.sendSetupElicitingStatusMessage(
                0, channelEndpoint.explicitControlAddress(), sessionId, streamId);
        }
    }

    void onRemoveSubscription(final ReceiveChannelEndpoint channelEndpoint, final int streamId)
    {
        channelEndpoint.dispatcher().removeSubscription(streamId);
    }

    void onRemoveSubscription(final ReceiveChannelEndpoint channelEndpoint, final int streamId, final int sessionId)
    {
        channelEndpoint.dispatcher().removeSubscription(streamId, sessionId);

        final ArrayList pendingSetupMessages = this.pendingSetupMessages;
        for (int lastIndex = pendingSetupMessages.size() - 1, i = lastIndex; i >= 0; i--)
        {
            final PendingSetupMessageFromSource pending = pendingSetupMessages.get(i);

            if (pending.channelEndpoint() == channelEndpoint &&
                pending.streamId() == streamId &&
                pending.sessionId() == sessionId)
            {
                ArrayListUtil.fastUnorderedRemove(pendingSetupMessages, i, lastIndex--);
                pending.removeFromDataPacketDispatcher();
            }
        }
    }

    void onNewPublicationImage(final ReceiveChannelEndpoint channelEndpoint, final PublicationImage image)
    {
        publicationImages = ArrayUtil.add(publicationImages, image);
        channelEndpoint.dispatcher().addPublicationImage(image);
    }

    void onRegisterReceiveChannelEndpoint(final ReceiveChannelEndpoint channelEndpoint)
    {
        if (!channelEndpoint.hasDestinationControl())
        {
            channelEndpoint.openChannel(conductorProxy);
            channelEndpoint.registerForRead(dataTransportPoller);
            channelEndpoint.indicateActive();

            if (channelEndpoint.hasExplicitControl())
            {
                addPendingSetupMessage(0, 0, 0, channelEndpoint, true, channelEndpoint.explicitControlAddress());
                channelEndpoint.sendSetupElicitingStatusMessage(0, channelEndpoint.explicitControlAddress(), 0, 0);
            }
        }
        else
        {
            channelEndpoint.indicateActive();
        }
    }

    void onCloseReceiveChannelEndpoint(final ReceiveChannelEndpoint channelEndpoint)
    {
        final ArrayList pendingSetupMessages = this.pendingSetupMessages;
        for (int lastIndex = pendingSetupMessages.size() - 1, i = lastIndex; i >= 0; i--)
        {
            final PendingSetupMessageFromSource pending = pendingSetupMessages.get(i);

            if (pending.channelEndpoint() == channelEndpoint)
            {
                ArrayListUtil.fastUnorderedRemove(pendingSetupMessages, i, lastIndex--);
                pending.removeFromDataPacketDispatcher();
            }
        }

        channelEndpoint.closeMultiRcvDestinationTransports(dataTransportPoller);
        channelEndpoint.close();
        channelEndpoint.closeMultiRcvDestinationIndicators(conductorProxy);
    }

    void onRemoveCoolDown(final ReceiveChannelEndpoint channelEndpoint, final int sessionId, final int streamId)
    {
        channelEndpoint.dispatcher().removeCoolDown(sessionId, streamId);
    }

    void onAddDestination(final ReceiveChannelEndpoint channelEndpoint, final ReceiveDestinationTransport transport)
    {
        transport.openChannel(conductorProxy, channelEndpoint.statusIndicatorCounter());

        final int transportIndex = channelEndpoint.addDestination(transport);
        final SelectionKey key = dataTransportPoller.registerForRead(channelEndpoint, transport, transportIndex);
        transport.selectionKey(key);

        if (transport.hasExplicitControl())
        {
            addPendingSetupMessage(0, 0, transportIndex, channelEndpoint, true, transport.explicitControlAddress());
            channelEndpoint.sendSetupElicitingStatusMessage(transportIndex, transport.explicitControlAddress(), 0, 0);
        }

        for (final PublicationImage image : publicationImages)
        {
            if (channelEndpoint == image.channelEndpoint())
            {
                image.addDestination(transportIndex, transport);
            }
        }
    }

    void onRemoveDestination(final ReceiveChannelEndpoint channelEndpoint, final UdpChannel udpChannel)
    {
        final int transportIndex = channelEndpoint.destination(udpChannel);
        if (ArrayUtil.UNKNOWN_INDEX != transportIndex)
        {
            final ReceiveDestinationTransport transport = channelEndpoint.destination(transportIndex);

            dataTransportPoller.cancelRead(channelEndpoint, transport);
            channelEndpoint.removeDestination(transportIndex);
            transport.closeTransport();
            dataTransportPoller.selectNowWithoutProcessing();

            for (final PublicationImage image : publicationImages)
            {
                if (channelEndpoint == image.channelEndpoint())
                {
                    image.removeDestination(transportIndex);
                }
            }

            conductorProxy.closeReceiveDestinationIndicators(transport);
        }
    }

    void onResolutionChange(
        final ReceiveChannelEndpoint channelEndpoint, final UdpChannel channel, final InetSocketAddress newAddress)
    {
        final int transportIndex = channelEndpoint.hasDestinationControl() ? channelEndpoint.destination(channel) : 0;

        for (int i = 0, size = pendingSetupMessages.size(); i < size; i++)
        {
            final PendingSetupMessageFromSource pending = pendingSetupMessages.get(i);

            if (pending.channelEndpoint() == channelEndpoint &&
                pending.isPeriodic() &&
                pending.transportIndex() == transportIndex)
            {
                pending.controlAddress(newAddress);
                resolutionChanges.getAndAddOrdered(1);
            }
        }

        channelEndpoint.updateControlAddress(transportIndex, newAddress);
    }

    private void checkPendingSetupMessages(final long nowNs)
    {
        for (int lastIndex = pendingSetupMessages.size() - 1, i = lastIndex; i >= 0; i--)
        {
            final PendingSetupMessageFromSource pending = pendingSetupMessages.get(i);

            if ((pending.timeOfStatusMessageNs() + PENDING_SETUPS_TIMEOUT_NS) - nowNs < 0)
            {
                if (!pending.isPeriodic())
                {
                    ArrayListUtil.fastUnorderedRemove(pendingSetupMessages, i, lastIndex--);
                    pending.removeFromDataPacketDispatcher();
                }
                else if (pending.shouldElicitSetupMessage())
                {
                    pending.timeOfStatusMessageNs(nowNs);
                    pending.channelEndpoint().sendSetupElicitingStatusMessage(
                        pending.transportIndex(), pending.controlAddress(), pending.sessionId(), pending.streamId());
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy