/* * Copyright 2014-2017 Real Logic Ltd. * * 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 io.aeron.driver; import io.aeron.driver.cmd.ReceiverCmd; import io.aeron.driver.media.DataTransportPoller; import io.aeron.driver.media.ReceiveChannelEndpoint; import org.agrona.collections.ArrayListUtil; import org.agrona.concurrent.Agent; import org.agrona.concurrent.status.AtomicCounter; import org.agrona.concurrent.NanoClock; import org.agrona.concurrent.OneToOneConcurrentArrayQueue; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.function.Consumer; import static io.aeron.driver.Configuration.PENDING_SETUPS_TIMEOUT_NS; import static io.aeron.driver.status.SystemCounterDescriptor.BYTES_RECEIVED; /** * Receiver agent for JVM based media driver, uses an event loop with command buffer */ public class Receiver implements Agent, Consumer<ReceiverCmd> { private final DataTransportPoller dataTransportPoller; private final OneToOneConcurrentArrayQueue<ReceiverCmd> commandQueue; private final AtomicCounter totalBytesReceived; private final NanoClock clock; private final ArrayList<PublicationImage> publicationImages = new ArrayList<>(); private final ArrayList<PendingSetupMessageFromSource> pendingSetupMessages = new ArrayList<>(); public Receiver(final MediaDriver.Context ctx) { dataTransportPoller = ctx.dataTransportPoller(); commandQueue = ctx.receiverCommandQueue(); totalBytesReceived = ctx.systemCounters().get(BYTES_RECEIVED); clock = ctx.nanoClock(); } public void onClose() { dataTransportPoller.close(); } public String roleName() { return "receiver"; } public int doWork() throws Exception { int workCount = commandQueue.drain(this); final int bytesReceived = dataTransportPoller.pollTransports(); final long nowNs = clock.nanoTime(); final ArrayList<PublicationImage> publicationImages = this.publicationImages; for (int lastIndex = publicationImages.size() - 1, i = lastIndex; i >= 0; i--) { final PublicationImage image = publicationImages.get(i); if (!image.checkForActivity(nowNs)) { image.removeFromDispatcher(); ArrayListUtil.fastUnorderedRemove(publicationImages, i, lastIndex); lastIndex--; } else { workCount += image.sendPendingStatusMessage(); workCount += image.processPendingLoss(); workCount += image.initiateAnyRttMeasurements(nowNs); } } checkPendingSetupMessages(nowNs); totalBytesReceived.addOrdered(bytesReceived); return workCount + bytesReceived; } public void addPendingSetupMessage( final int sessionId, final int streamId, final ReceiveChannelEndpoint channelEndpoint, final boolean periodic, final InetSocketAddress controlAddress) { final PendingSetupMessageFromSource cmd = new PendingSetupMessageFromSource( sessionId, streamId, channelEndpoint, periodic, controlAddress); cmd.timeOfStatusMessageNs(clock.nanoTime()); pendingSetupMessages.add(cmd); } public void onAddSubscription(final ReceiveChannelEndpoint channelEndpoint, final int streamId) { channelEndpoint.addSubscription(streamId); } public void onRemoveSubscription(final ReceiveChannelEndpoint channelEndpoint, final int streamId) { channelEndpoint.removeSubscription(streamId); } public void onNewPublicationImage(final ReceiveChannelEndpoint channelEndpoint, final PublicationImage image) { publicationImages.add(image); channelEndpoint.addPublicationImage(image); } public void onRegisterReceiveChannelEndpoint(final ReceiveChannelEndpoint channelEndpoint) { channelEndpoint.openChannel(); channelEndpoint.registerForRead(dataTransportPoller); channelEndpoint.indicateActive(); if (channelEndpoint.hasExplicitControl()) { addPendingSetupMessage(0, 0, channelEndpoint, true, channelEndpoint.explicitControlAddress()); channelEndpoint.sendSetupElicitingStatusMessage(channelEndpoint.explicitControlAddress(), 0, 0); } } public void onCloseReceiveChannelEndpoint(final ReceiveChannelEndpoint channelEndpoint) { channelEndpoint.close(); } public void onRemoveCoolDown(final ReceiveChannelEndpoint channelEndpoint, final int sessionId, final int streamId) { channelEndpoint.removeCoolDown(sessionId, streamId); } public void accept(final ReceiverCmd cmd) { cmd.execute(this); } private void checkPendingSetupMessages(final long nowNs) { final ArrayList<PendingSetupMessageFromSource> pendingSetupMessages = this.pendingSetupMessages; for (int lastIndex = pendingSetupMessages.size() - 1, i = lastIndex; i >= 0; i--) { final PendingSetupMessageFromSource pending = pendingSetupMessages.get(i); if (nowNs > (pending.timeOfStatusMessageNs() + PENDING_SETUPS_TIMEOUT_NS)) { if (!pending.isPeriodic()) { ArrayListUtil.fastUnorderedRemove(pendingSetupMessages, i, lastIndex); lastIndex--; pending.removeFromDataPacketDispatcher(); } else if (pending.shouldElicitSetupMessage()) { pending.channelEndpoint().sendSetupElicitingStatusMessage( pending.controlAddress(), pending.sessionId(), pending.streamId()); pending.timeOfStatusMessageNs(nowNs); } } } } }