/* * Copyright 2014-2020 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.media; import io.aeron.CommonContext; import io.aeron.driver.DriverConductorProxy; import org.agrona.CloseHelper; import org.agrona.ErrorHandler; import org.agrona.collections.ArrayUtil; import org.agrona.concurrent.NanoClock; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import static io.aeron.driver.media.UdpChannelTransport.sendError; final class MultiRcvDestination { private static final ReceiveDestinationTransport[] EMPTY_TRANSPORTS = new ReceiveDestinationTransport[0]; private final long destinationEndpointTimeoutNs; private final ErrorHandler errorHandler; private final NanoClock nanoClock; private ReceiveDestinationTransport[] transports = EMPTY_TRANSPORTS; private int numDestinations = 0; MultiRcvDestination(final NanoClock nanoClock, final long timeoutNs, final ErrorHandler errorHandler) { this.nanoClock = nanoClock; this.destinationEndpointTimeoutNs = timeoutNs; this.errorHandler = errorHandler; } void close(final DataTransportPoller poller) { for (final ReceiveDestinationTransport transport : transports) { CloseHelper.close(errorHandler, transport); if (null != poller) { poller.selectNowWithoutProcessing(); } } } int addDestination(final ReceiveDestinationTransport transport) { int index = transports.length; for (int i = 0, length = transports.length; i < length; i++) { if (null == transports[i]) { index = i; break; } } transports = ArrayUtil.ensureCapacity(transports, index + 1); transports[index] = transport; numDestinations++; return index; } void removeDestination(final int transportIndex) { transports[transportIndex] = null; numDestinations--; } boolean hasDestination(final int transportIndex) { return numDestinations > transportIndex && null != transports[transportIndex]; } ReceiveDestinationTransport transport(final int transportIndex) { return transports[transportIndex]; } int transport(final UdpChannel udpChannel) { final ReceiveDestinationTransport[] transports = this.transports; int index = ArrayUtil.UNKNOWN_INDEX; for (int i = 0, length = transports.length; i < length; i++) { final ReceiveDestinationTransport transport = transports[i]; if (null != transport && transport.udpChannel().equals(udpChannel)) { index = i; break; } } return index; } void checkForReResolution( final ReceiveChannelEndpoint channelEndpoint, final long nowNs, final DriverConductorProxy conductorProxy) { for (final ReceiveDestinationTransport transport : transports) { if (null != transport) { final UdpChannel udpChannel = transport.udpChannel(); if (udpChannel.hasExplicitControl() && (transport.timeOfLastActivityNs() + destinationEndpointTimeoutNs) < nowNs) { conductorProxy.reResolveControl( udpChannel.channelUri().get(CommonContext.MDC_CONTROL_PARAM_NAME), udpChannel, channelEndpoint, transport.currentControlAddress()); transport.timeOfLastActivityNs(nowNs); } } } } void updateControlAddress(final int transportIndex, final InetSocketAddress newAddress) { if (ArrayUtil.UNKNOWN_INDEX != transportIndex) { final ReceiveDestinationTransport transport = transports[transportIndex]; if (null != transport) { transport.currentControlAddress(newAddress); } } } int sendToAll(final ImageConnection[] imageConnections, final ByteBuffer buffer, final int bytesToSend) { final ReceiveDestinationTransport[] transports = this.transports; final long nowNs = nanoClock.nanoTime(); int minBytesSent = bytesToSend; for (int lastIndex = imageConnections.length - 1, i = lastIndex; i >= 0; i--) { final ImageConnection connection = imageConnections[i]; if (null != connection) { final UdpChannelTransport transport = transports[i]; if (null != transport && ((connection.timeOfLastActivityNs + destinationEndpointTimeoutNs) - nowNs > 0)) { buffer.position(0); minBytesSent = Math.min(minBytesSent, sendTo(transport, buffer, connection.controlAddress)); } } } return minBytesSent; } static int sendTo( final UdpChannelTransport transport, final ByteBuffer buffer, final InetSocketAddress remoteAddress) { final int remaining = buffer.remaining(); int bytesSent = 0; try { if (null != transport && null != transport.sendDatagramChannel && transport.sendDatagramChannel.isOpen()) { transport.sendHook(buffer, remoteAddress); bytesSent = transport.sendDatagramChannel.send(buffer, remoteAddress); } } catch (final IOException ex) { sendError(remaining, ex, remoteAddress); } return bytesSent; } }