Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.aeron.ClientConductor Maven / Gradle / Ivy
Go to download
Efficient reliable UDP unicast, UDP multicast, and IPC transport protocol.
/*
* Copyright 2014-2021 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;
import io.aeron.exceptions.*;
import io.aeron.status.ChannelEndpointStatus;
import io.aeron.status.HeartbeatTimestamp;
import org.agrona.*;
import org.agrona.collections.ArrayListUtil;
import org.agrona.collections.Long2ObjectHashMap;
import org.agrona.collections.LongHashSet;
import org.agrona.concurrent.*;
import org.agrona.concurrent.status.AtomicCounter;
import org.agrona.concurrent.status.CountersManager;
import org.agrona.concurrent.status.CountersReader;
import org.agrona.concurrent.status.UnsafeBufferPosition;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import static io.aeron.Aeron.Configuration.IDLE_SLEEP_MS;
import static io.aeron.Aeron.Configuration.IDLE_SLEEP_NS;
import static io.aeron.Aeron.NULL_VALUE;
import static io.aeron.ErrorCode.CHANNEL_ENDPOINT_ERROR;
import static io.aeron.status.HeartbeatTimestamp.HEARTBEAT_TYPE_ID;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* Client conductor receives responses and notifications from Media Driver and acts on them in addition to forwarding
* commands from the Client API to the Media Driver conductor.
*/
final class ClientConductor implements Agent
{
private static final long NO_CORRELATION_ID = NULL_VALUE;
private static final long EXPLICIT_CLOSE_LINGER_NS = TimeUnit.SECONDS.toNanos(1);
private final long keepAliveIntervalNs;
private final long driverTimeoutMs;
private final long driverTimeoutNs;
private final long interServiceTimeoutNs;
private long timeOfLastKeepAliveNs;
private long timeOfLastServiceNs;
private boolean isClosed;
private boolean isInCallback;
private boolean isTerminating;
private RegistrationException driverException;
private final Aeron.Context ctx;
private final Aeron aeron;
private final Lock clientLock;
private final EpochClock epochClock;
private final NanoClock nanoClock;
private final IdleStrategy awaitingIdleStrategy;
private final DriverEventsAdapter driverEventsAdapter;
private final LogBuffersFactory logBuffersFactory;
private final Long2ObjectHashMap logBuffersByIdMap = new Long2ObjectHashMap<>();
private final ArrayList lingeringLogBuffers = new ArrayList<>();
private final Long2ObjectHashMap resourceByRegIdMap = new Long2ObjectHashMap<>();
private final Long2ObjectHashMap stashedChannelByRegistrationId = new Long2ObjectHashMap<>();
private final LongHashSet asyncCommandIdSet = new LongHashSet();
private final AvailableImageHandler defaultAvailableImageHandler;
private final UnavailableImageHandler defaultUnavailableImageHandler;
private final Long2ObjectHashMap availableCounterHandlerById = new Long2ObjectHashMap<>();
private final Long2ObjectHashMap unavailableCounterHandlerById =
new Long2ObjectHashMap<>();
private final Long2ObjectHashMap closeHandlerByIdMap = new Long2ObjectHashMap<>();
private final DriverProxy driverProxy;
private final AgentInvoker driverAgentInvoker;
private final UnsafeBuffer counterValuesBuffer;
private final CountersReader countersReader;
private AtomicCounter heartbeatTimestamp;
ClientConductor(final Aeron.Context ctx, final Aeron aeron)
{
this.ctx = ctx;
this.aeron = aeron;
clientLock = ctx.clientLock();
epochClock = ctx.epochClock();
nanoClock = ctx.nanoClock();
awaitingIdleStrategy = ctx.awaitingIdleStrategy();
driverProxy = ctx.driverProxy();
logBuffersFactory = ctx.logBuffersFactory();
keepAliveIntervalNs = ctx.keepAliveIntervalNs();
driverTimeoutMs = ctx.driverTimeoutMs();
driverTimeoutNs = MILLISECONDS.toNanos(driverTimeoutMs);
interServiceTimeoutNs = ctx.interServiceTimeoutNs();
defaultAvailableImageHandler = ctx.availableImageHandler();
defaultUnavailableImageHandler = ctx.unavailableImageHandler();
driverEventsAdapter = new DriverEventsAdapter(ctx.clientId(), ctx.toClientBuffer(), this, asyncCommandIdSet);
driverAgentInvoker = ctx.driverAgentInvoker();
counterValuesBuffer = ctx.countersValuesBuffer();
countersReader = new CountersReader(ctx.countersMetaDataBuffer(), ctx.countersValuesBuffer(), US_ASCII);
if (null != ctx.availableCounterHandler())
{
availableCounterHandlerById.put(aeron.nextCorrelationId(), ctx.availableCounterHandler());
}
if (null != ctx.unavailableCounterHandler())
{
unavailableCounterHandlerById.put(aeron.nextCorrelationId(), ctx.unavailableCounterHandler());
}
if (null != ctx.closeHandler())
{
closeHandlerByIdMap.put(aeron.nextCorrelationId(), ctx.closeHandler());
}
final long nowNs = nanoClock.nanoTime();
timeOfLastKeepAliveNs = nowNs;
timeOfLastServiceNs = nowNs;
}
public void onClose()
{
boolean isInterrupted = false;
clientLock.lock();
try
{
if (!isClosed)
{
if (!aeron.isClosed())
{
aeron.internalClose();
}
final boolean isTerminating = this.isTerminating;
this.isTerminating = true;
forceCloseResources();
notifyCloseHandlers();
try
{
if (isTerminating)
{
Thread.sleep(IDLE_SLEEP_MS);
}
Thread.sleep(NANOSECONDS.toMillis(ctx.closeLingerDurationNs()));
}
catch (final InterruptedException ignore)
{
isInterrupted = true;
}
for (final LogBuffers lingeringLogBuffer : lingeringLogBuffers)
{
CloseHelper.close(ctx.errorHandler(), lingeringLogBuffer);
}
driverProxy.clientClose();
ctx.close();
ctx.countersMetaDataBuffer().wrap(0, 0);
ctx.countersValuesBuffer().wrap(0, 0);
}
}
finally
{
isClosed = true;
if (isInterrupted)
{
Thread.currentThread().interrupt();
}
clientLock.unlock();
}
}
public int doWork()
{
int workCount = 0;
if (clientLock.tryLock())
{
try
{
if (isTerminating)
{
throw new AgentTerminationException();
}
workCount = service(NO_CORRELATION_ID);
}
finally
{
clientLock.unlock();
}
}
return workCount;
}
public String roleName()
{
return "aeron-client-conductor";
}
boolean isClosed()
{
return isClosed;
}
boolean isTerminating()
{
return isTerminating;
}
void onError(final long correlationId, final int codeValue, final ErrorCode errorCode, final String message)
{
driverException = new RegistrationException(correlationId, codeValue, errorCode, message);
final Object resource = resourceByRegIdMap.get(correlationId);
if (resource instanceof Subscription)
{
final Subscription subscription = (Subscription)resource;
subscription.internalClose(NULL_VALUE);
resourceByRegIdMap.remove(correlationId);
}
else if (asyncCommandIdSet.remove(correlationId))
{
stashedChannelByRegistrationId.remove(correlationId);
handleError(new RegistrationException(correlationId, codeValue, errorCode, message));
}
}
void onAsyncError(final long correlationId, final int codeValue, final ErrorCode errorCode, final String message)
{
stashedChannelByRegistrationId.remove(correlationId);
handleError(new RegistrationException(correlationId, codeValue, errorCode, message));
}
void onChannelEndpointError(final long correlationId, final String message)
{
final int statusIndicatorId = (int)correlationId;
for (final Object resource : resourceByRegIdMap.values())
{
if (resource instanceof Subscription)
{
if (((Subscription)resource).channelStatusId() == statusIndicatorId)
{
handleError(new ChannelEndpointException(statusIndicatorId, message));
}
}
else if (resource instanceof Publication)
{
if (((Publication)resource).channelStatusId() == statusIndicatorId)
{
handleError(new ChannelEndpointException(statusIndicatorId, message));
}
}
}
if (asyncCommandIdSet.remove(correlationId))
{
stashedChannelByRegistrationId.remove(correlationId);
handleError(new RegistrationException(
correlationId, CHANNEL_ENDPOINT_ERROR.value(), CHANNEL_ENDPOINT_ERROR, message));
}
}
void onNewPublication(
final long correlationId,
final long registrationId,
final int streamId,
final int sessionId,
final int publicationLimitId,
final int statusIndicatorId,
final String logFileName)
{
final String stashedChannel = stashedChannelByRegistrationId.remove(correlationId);
final ConcurrentPublication publication = new ConcurrentPublication(
this,
stashedChannel,
streamId,
sessionId,
new UnsafeBufferPosition(counterValuesBuffer, publicationLimitId),
statusIndicatorId,
logBuffers(registrationId, logFileName, stashedChannel),
registrationId,
correlationId);
resourceByRegIdMap.put(correlationId, publication);
asyncCommandIdSet.remove(correlationId);
}
void onNewExclusivePublication(
final long correlationId,
final long registrationId,
final int streamId,
final int sessionId,
final int publicationLimitId,
final int statusIndicatorId,
final String logFileName)
{
if (correlationId != registrationId)
{
handleError(new IllegalStateException(
"correlationId=" + correlationId + " registrationId=" + registrationId));
}
final String stashedChannel = stashedChannelByRegistrationId.remove(correlationId);
final ExclusivePublication publication = new ExclusivePublication(
this,
stashedChannel,
streamId,
sessionId,
new UnsafeBufferPosition(counterValuesBuffer, publicationLimitId),
statusIndicatorId,
logBuffers(registrationId, logFileName, stashedChannel),
registrationId,
correlationId);
resourceByRegIdMap.put(correlationId, publication);
asyncCommandIdSet.remove(correlationId);
}
void onNewSubscription(final long correlationId, final int statusIndicatorId)
{
final Subscription subscription = (Subscription)resourceByRegIdMap.get(correlationId);
subscription.channelStatusId(statusIndicatorId);
}
void onAvailableImage(
final long correlationId,
final int sessionId,
final long subscriptionRegistrationId,
final int subscriberPositionId,
final String logFileName,
final String sourceIdentity)
{
final Subscription subscription = (Subscription)resourceByRegIdMap.get(subscriptionRegistrationId);
if (null != subscription)
{
final Image image = new Image(
subscription,
sessionId,
new UnsafeBufferPosition(counterValuesBuffer, subscriberPositionId),
logBuffers(correlationId, logFileName, subscription.channel()),
ctx.subscriberErrorHandler(),
sourceIdentity,
correlationId);
final AvailableImageHandler handler = subscription.availableImageHandler();
if (null != handler)
{
isInCallback = true;
try
{
handler.onAvailableImage(image);
}
catch (final Exception ex)
{
handleError(ex);
}
finally
{
isInCallback = false;
}
}
subscription.addImage(image);
}
}
void onUnavailableImage(final long correlationId, final long subscriptionRegistrationId)
{
final Subscription subscription = (Subscription)resourceByRegIdMap.get(subscriptionRegistrationId);
if (null != subscription)
{
final Image image = subscription.removeImage(correlationId);
if (null != image)
{
final UnavailableImageHandler handler = subscription.unavailableImageHandler();
if (null != handler)
{
notifyImageUnavailable(handler, image);
}
}
}
}
void onNewCounter(final long correlationId, final int counterId)
{
resourceByRegIdMap.put(correlationId, new Counter(correlationId, this, counterValuesBuffer, counterId));
onAvailableCounter(correlationId, counterId);
}
void onAvailableCounter(final long registrationId, final int counterId)
{
for (final AvailableCounterHandler handler : availableCounterHandlerById.values())
{
notifyCounterAvailable(registrationId, counterId, handler);
}
}
void onUnavailableCounter(final long registrationId, final int counterId)
{
notifyUnavailableCounterHandlers(registrationId, counterId);
}
void onClientTimeout()
{
if (!isClosed)
{
isTerminating = true;
forceCloseResources();
handleError(new ClientTimeoutException("client timeout from driver"));
}
}
CountersReader countersReader()
{
return countersReader;
}
void handleError(final Throwable ex)
{
if (!isClosed)
{
ctx.errorHandler().onError(ex);
}
}
ConcurrentPublication addPublication(final String channel, final int streamId)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long registrationId = driverProxy.addPublication(channel, streamId);
stashedChannelByRegistrationId.put(registrationId, channel);
awaitResponse(registrationId);
return (ConcurrentPublication)resourceByRegIdMap.get(registrationId);
}
finally
{
clientLock.unlock();
}
}
ExclusivePublication addExclusivePublication(final String channel, final int streamId)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long registrationId = driverProxy.addExclusivePublication(channel, streamId);
stashedChannelByRegistrationId.put(registrationId, channel);
awaitResponse(registrationId);
return (ExclusivePublication)resourceByRegIdMap.get(registrationId);
}
finally
{
clientLock.unlock();
}
}
long asyncAddPublication(final String channel, final int streamId)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long registrationId = driverProxy.addPublication(channel, streamId);
stashedChannelByRegistrationId.put(registrationId, channel);
asyncCommandIdSet.add(registrationId);
return registrationId;
}
finally
{
clientLock.unlock();
}
}
long asyncAddExclusivePublication(final String channel, final int streamId)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long registrationId = driverProxy.addExclusivePublication(channel, streamId);
stashedChannelByRegistrationId.put(registrationId, channel);
asyncCommandIdSet.add(registrationId);
return registrationId;
}
finally
{
clientLock.unlock();
}
}
ConcurrentPublication getPublication(final long registrationId)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
if (asyncCommandIdSet.contains(registrationId))
{
service(NO_CORRELATION_ID);
}
return (ConcurrentPublication)resourceByRegIdMap.get(registrationId);
}
finally
{
clientLock.unlock();
}
}
ExclusivePublication getExclusivePublication(final long registrationId)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
if (asyncCommandIdSet.contains(registrationId))
{
service(NO_CORRELATION_ID);
}
return (ExclusivePublication)resourceByRegIdMap.get(registrationId);
}
finally
{
clientLock.unlock();
}
}
void releasePublication(final Publication publication)
{
clientLock.lock();
try
{
if (isTerminating || isClosed)
{
return;
}
if (!publication.isClosed())
{
ensureNotReentrant();
publication.internalClose();
if (publication == resourceByRegIdMap.remove(publication.registrationId()))
{
releaseLogBuffers(
publication.logBuffers(), publication.originalRegistrationId(), EXPLICIT_CLOSE_LINGER_NS);
asyncCommandIdSet.add(driverProxy.removePublication(publication.registrationId()));
}
}
}
finally
{
clientLock.unlock();
}
}
Subscription addSubscription(final String channel, final int streamId)
{
return addSubscription(channel, streamId, defaultAvailableImageHandler, defaultUnavailableImageHandler);
}
Subscription addSubscription(
final String channel,
final int streamId,
final AvailableImageHandler availableImageHandler,
final UnavailableImageHandler unavailableImageHandler)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long correlationId = driverProxy.addSubscription(channel, streamId);
final Subscription subscription = new Subscription(
this,
channel,
streamId,
correlationId,
availableImageHandler,
unavailableImageHandler);
resourceByRegIdMap.put(correlationId, subscription);
awaitResponse(correlationId);
return subscription;
}
finally
{
clientLock.unlock();
}
}
void releaseSubscription(final Subscription subscription)
{
clientLock.lock();
try
{
if (isTerminating || isClosed)
{
return;
}
if (!subscription.isClosed())
{
ensureNotReentrant();
subscription.internalClose(EXPLICIT_CLOSE_LINGER_NS);
final long registrationId = subscription.registrationId();
if (subscription == resourceByRegIdMap.remove(registrationId))
{
asyncCommandIdSet.add(driverProxy.removeSubscription(registrationId));
}
}
}
finally
{
clientLock.unlock();
}
}
void addDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
awaitResponse(driverProxy.addDestination(registrationId, endpointChannel));
}
finally
{
clientLock.unlock();
}
}
void removeDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
awaitResponse(driverProxy.removeDestination(registrationId, endpointChannel));
}
finally
{
clientLock.unlock();
}
}
void addRcvDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
awaitResponse(driverProxy.addRcvDestination(registrationId, endpointChannel));
}
finally
{
clientLock.unlock();
}
}
void removeRcvDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
awaitResponse(driverProxy.removeRcvDestination(registrationId, endpointChannel));
}
finally
{
clientLock.unlock();
}
}
long asyncAddDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long correlationId = driverProxy.addDestination(registrationId, endpointChannel);
asyncCommandIdSet.add(correlationId);
return correlationId;
}
finally
{
clientLock.unlock();
}
}
long asyncRemoveDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long correlationId = driverProxy.removeDestination(registrationId, endpointChannel);
asyncCommandIdSet.add(correlationId);
return correlationId;
}
finally
{
clientLock.unlock();
}
}
long asyncAddRcvDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long correlationId = driverProxy.addRcvDestination(registrationId, endpointChannel);
asyncCommandIdSet.add(correlationId);
return correlationId;
}
finally
{
clientLock.unlock();
}
}
long asyncRemoveRcvDestination(final long registrationId, final String endpointChannel)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long correlationId = driverProxy.removeRcvDestination(registrationId, endpointChannel);
asyncCommandIdSet.add(correlationId);
return correlationId;
}
finally
{
clientLock.unlock();
}
}
boolean isCommandActive(final long correlationId)
{
clientLock.lock();
try
{
if (isClosed)
{
return false;
}
ensureActive();
if (asyncCommandIdSet.contains(correlationId))
{
service(NO_CORRELATION_ID);
}
return asyncCommandIdSet.contains(correlationId);
}
finally
{
clientLock.unlock();
}
}
boolean hasActiveCommands()
{
clientLock.lock();
try
{
if (isClosed)
{
return false;
}
ensureActive();
return !asyncCommandIdSet.isEmpty();
}
finally
{
clientLock.unlock();
}
}
Counter addCounter(
final int typeId,
final DirectBuffer keyBuffer,
final int keyOffset,
final int keyLength,
final DirectBuffer labelBuffer,
final int labelOffset,
final int labelLength)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
if (keyLength < 0 || keyLength > CountersManager.MAX_KEY_LENGTH)
{
throw new IllegalArgumentException("key length out of bounds: " + keyLength);
}
if (labelLength < 0 || labelLength > CountersManager.MAX_LABEL_LENGTH)
{
throw new IllegalArgumentException("label length out of bounds: " + labelLength);
}
final long registrationId = driverProxy.addCounter(
typeId, keyBuffer, keyOffset, keyLength, labelBuffer, labelOffset, labelLength);
awaitResponse(registrationId);
return (Counter)resourceByRegIdMap.get(registrationId);
}
finally
{
clientLock.unlock();
}
}
Counter addCounter(final int typeId, final String label)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
if (label.length() > CountersManager.MAX_LABEL_LENGTH)
{
throw new IllegalArgumentException("label length exceeds MAX_LABEL_LENGTH: " + label.length());
}
final long registrationId = driverProxy.addCounter(typeId, label);
awaitResponse(registrationId);
return (Counter)resourceByRegIdMap.get(registrationId);
}
finally
{
clientLock.unlock();
}
}
long addAvailableCounterHandler(final AvailableCounterHandler handler)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long registrationId = aeron.nextCorrelationId();
availableCounterHandlerById.put(registrationId, handler);
return registrationId;
}
finally
{
clientLock.unlock();
}
}
boolean removeAvailableCounterHandler(final long registrationId)
{
clientLock.lock();
try
{
return availableCounterHandlerById.remove(registrationId) != null;
}
finally
{
clientLock.unlock();
}
}
boolean removeAvailableCounterHandler(final AvailableCounterHandler handler)
{
clientLock.lock();
try
{
if (isTerminating || isClosed)
{
return false;
}
ensureNotReentrant();
final Long2ObjectHashMap.ValueIterator iterator =
availableCounterHandlerById.values().iterator();
while (iterator.hasNext())
{
if (handler == iterator.next())
{
iterator.remove();
return true;
}
}
return false;
}
finally
{
clientLock.unlock();
}
}
long addUnavailableCounterHandler(final UnavailableCounterHandler handler)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long registrationId = aeron.nextCorrelationId();
unavailableCounterHandlerById.put(registrationId, handler);
return registrationId;
}
finally
{
clientLock.unlock();
}
}
boolean removeUnavailableCounterHandler(final long registrationId)
{
clientLock.lock();
try
{
return unavailableCounterHandlerById.remove(registrationId) != null;
}
finally
{
clientLock.unlock();
}
}
boolean removeUnavailableCounterHandler(final UnavailableCounterHandler handler)
{
clientLock.lock();
try
{
if (isTerminating || isClosed)
{
return false;
}
ensureNotReentrant();
final Long2ObjectHashMap.ValueIterator iterator =
unavailableCounterHandlerById.values().iterator();
while (iterator.hasNext())
{
if (handler == iterator.next())
{
iterator.remove();
return true;
}
}
return false;
}
finally
{
clientLock.unlock();
}
}
long addCloseHandler(final Runnable handler)
{
clientLock.lock();
try
{
ensureActive();
ensureNotReentrant();
final long registrationId = aeron.nextCorrelationId();
closeHandlerByIdMap.put(registrationId, handler);
return registrationId;
}
finally
{
clientLock.unlock();
}
}
boolean removeCloseHandler(final long registrationId)
{
clientLock.lock();
try
{
return closeHandlerByIdMap.remove(registrationId) != null;
}
finally
{
clientLock.unlock();
}
}
boolean removeCloseHandler(final Runnable handler)
{
clientLock.lock();
try
{
if (isTerminating || isClosed)
{
return false;
}
ensureNotReentrant();
final Long2ObjectHashMap.ValueIterator iterator = closeHandlerByIdMap.values().iterator();
while (iterator.hasNext())
{
if (handler == iterator.next())
{
iterator.remove();
return true;
}
}
return false;
}
finally
{
clientLock.unlock();
}
}
void releaseCounter(final Counter counter)
{
clientLock.lock();
try
{
if (isTerminating || isClosed)
{
return;
}
ensureNotReentrant();
final long registrationId = counter.registrationId();
if (counter == resourceByRegIdMap.remove(registrationId))
{
asyncCommandIdSet.add(driverProxy.removeCounter(registrationId));
}
}
finally
{
clientLock.unlock();
}
}
void releaseLogBuffers(
final LogBuffers logBuffers, final long registrationId, final long lingerDurationNs)
{
if (logBuffers.decRef() == 0)
{
lingeringLogBuffers.add(logBuffers);
logBuffersByIdMap.remove(registrationId);
final long lingerNs = NULL_VALUE == lingerDurationNs ? ctx.resourceLingerDurationNs() : lingerDurationNs;
logBuffers.lingerDeadlineNs(nanoClock.nanoTime() + lingerNs);
}
}
DriverEventsAdapter driverListenerAdapter()
{
return driverEventsAdapter;
}
long channelStatus(final int channelStatusId)
{
switch (channelStatusId)
{
case 0:
return ChannelEndpointStatus.INITIALIZING;
case ChannelEndpointStatus.NO_ID_ALLOCATED:
return ChannelEndpointStatus.ACTIVE;
default:
return countersReader.getCounterValue(channelStatusId);
}
}
void closeImages(
final Image[] images, final UnavailableImageHandler unavailableImageHandler, final long lingerNs)
{
for (final Image image : images)
{
image.close();
}
for (final Image image : images)
{
releaseLogBuffers(image.logBuffers(), image.correlationId(), lingerNs);
}
if (null != unavailableImageHandler)
{
for (final Image image : images)
{
notifyImageUnavailable(unavailableImageHandler, image);
}
}
}
private void ensureActive()
{
if (isClosed)
{
throw new AeronException("Aeron client is closed");
}
if (isTerminating)
{
throw new AeronException("Aeron client is terminating");
}
}
private void ensureNotReentrant()
{
if (isInCallback)
{
throw new AeronException("reentrant calls not permitted during callbacks");
}
}
private LogBuffers logBuffers(final long registrationId, final String logFileName, final String channel)
{
LogBuffers logBuffers = logBuffersByIdMap.get(registrationId);
if (null == logBuffers)
{
logBuffers = logBuffersFactory.map(logFileName);
if (ctx.preTouchMappedMemory() && !channel.contains("sparse=true"))
{
logBuffers.preTouch();
}
logBuffersByIdMap.put(registrationId, logBuffers);
}
logBuffers.incRef();
return logBuffers;
}
private int service(final long correlationId)
{
int workCount = 0;
try
{
workCount += checkTimeouts(nanoClock.nanoTime());
workCount += driverEventsAdapter.receive(correlationId);
}
catch (final AgentTerminationException ex)
{
if (isClientApiCall(correlationId))
{
isTerminating = true;
forceCloseResources();
}
throw ex;
}
catch (final Exception ex)
{
if (driverEventsAdapter.isInvalid())
{
isTerminating = true;
forceCloseResources();
if (!isClientApiCall(correlationId))
{
throw new AeronException("Driver events adapter is invalid", ex);
}
}
if (isClientApiCall(correlationId))
{
throw ex;
}
handleError(ex);
}
return workCount;
}
private static boolean isClientApiCall(final long correlationId)
{
return correlationId != NO_CORRELATION_ID;
}
private void awaitResponse(final long correlationId)
{
final long nowNs = nanoClock.nanoTime();
final long deadlineNs = nowNs + driverTimeoutNs;
checkTimeouts(nowNs);
awaitingIdleStrategy.reset();
do
{
if (null == driverAgentInvoker)
{
awaitingIdleStrategy.idle();
}
else
{
driverAgentInvoker.invoke();
}
service(correlationId);
if (driverEventsAdapter.receivedCorrelationId() == correlationId)
{
stashedChannelByRegistrationId.remove(correlationId);
final RegistrationException ex = driverException;
if (null != ex)
{
driverException = null;
throw ex;
}
return;
}
if (Thread.currentThread().isInterrupted())
{
isTerminating = true;
throw new AeronException("unexpected interrupt");
}
}
while (deadlineNs - nanoClock.nanoTime() > 0);
throw new DriverTimeoutException("no response from MediaDriver within (ns): " + driverTimeoutNs);
}
private int checkTimeouts(final long nowNs)
{
int workCount = 0;
if ((timeOfLastServiceNs + IDLE_SLEEP_NS) - nowNs < 0)
{
checkServiceInterval(nowNs);
timeOfLastServiceNs = nowNs;
workCount += checkLiveness(nowNs);
workCount += checkLingeringResources(nowNs);
}
return workCount;
}
private void checkServiceInterval(final long nowNs)
{
if ((timeOfLastServiceNs + interServiceTimeoutNs) - nowNs < 0)
{
isTerminating = true;
forceCloseResources();
throw new ConductorServiceTimeoutException(
"service interval exceeded (ns): timeout=" + interServiceTimeoutNs +
", interval=" + (nowNs - timeOfLastServiceNs));
}
}
private int checkLiveness(final long nowNs)
{
if ((timeOfLastKeepAliveNs + keepAliveIntervalNs) - nowNs < 0)
{
final long nowMs = epochClock.time();
final long lastKeepAliveMs = driverProxy.timeOfLastDriverKeepaliveMs();
if (nowMs > (lastKeepAliveMs + driverTimeoutMs))
{
isTerminating = true;
forceCloseResources();
throw new DriverTimeoutException(
"MediaDriver keepalive (ms): age=" +
(nowMs - lastKeepAliveMs) + " > timeout=" + driverTimeoutMs);
}
if (null == heartbeatTimestamp)
{
final int counterId = HeartbeatTimestamp.findCounterIdByRegistrationId(
countersReader, HEARTBEAT_TYPE_ID, ctx.clientId());
if (counterId != CountersReader.NULL_COUNTER_ID)
{
heartbeatTimestamp = new AtomicCounter(counterValuesBuffer, counterId);
heartbeatTimestamp.setOrdered(nowMs);
timeOfLastKeepAliveNs = nowNs;
}
}
else
{
final int counterId = heartbeatTimestamp.id();
if (!HeartbeatTimestamp.isActive(countersReader, counterId, HEARTBEAT_TYPE_ID, ctx.clientId()))
{
isTerminating = true;
forceCloseResources();
throw new AeronException("unexpected close of heartbeat timestamp counter: " + counterId);
}
heartbeatTimestamp.setOrdered(nowMs);
timeOfLastKeepAliveNs = nowNs;
}
return 1;
}
return 0;
}
private int checkLingeringResources(final long nowNs)
{
int workCount = 0;
for (int lastIndex = lingeringLogBuffers.size() - 1, i = lastIndex; i >= 0; i--)
{
final LogBuffers logBuffers = lingeringLogBuffers.get(i);
if (logBuffers.lingerDeadlineNs() - nowNs < 0)
{
ArrayListUtil.fastUnorderedRemove(lingeringLogBuffers, i, lastIndex--);
CloseHelper.close(ctx.errorHandler(), logBuffers);
workCount += 1;
}
}
return workCount;
}
private void forceCloseResources()
{
for (final Object resource : resourceByRegIdMap.values())
{
if (resource instanceof Subscription)
{
final Subscription subscription = (Subscription)resource;
subscription.internalClose(NULL_VALUE);
}
else if (resource instanceof Publication)
{
final Publication publication = (Publication)resource;
publication.internalClose();
releaseLogBuffers(publication.logBuffers(), publication.originalRegistrationId(), NULL_VALUE);
}
else if (resource instanceof Counter)
{
final Counter counter = (Counter)resource;
counter.internalClose();
notifyUnavailableCounterHandlers(counter.registrationId(), counter.id());
}
}
resourceByRegIdMap.clear();
}
private void notifyUnavailableCounterHandlers(final long registrationId, final int counterId)
{
for (final UnavailableCounterHandler handler : unavailableCounterHandlerById.values())
{
isInCallback = true;
try
{
handler.onUnavailableCounter(countersReader, registrationId, counterId);
}
catch (final AgentTerminationException ex)
{
if (!isTerminating)
{
throw ex;
}
handleError(ex);
}
catch (final Exception ex)
{
handleError(ex);
}
finally
{
isInCallback = false;
}
}
}
private void notifyImageUnavailable(final UnavailableImageHandler handler, final Image image)
{
isInCallback = true;
try
{
handler.onUnavailableImage(image);
}
catch (final AgentTerminationException ex)
{
if (!isTerminating)
{
throw ex;
}
handleError(ex);
}
catch (final Exception ex)
{
handleError(ex);
}
finally
{
isInCallback = false;
}
}
private void notifyCounterAvailable(
final long registrationId, final int counterId, final AvailableCounterHandler handler)
{
isInCallback = true;
try
{
handler.onAvailableCounter(countersReader, registrationId, counterId);
}
catch (final AgentTerminationException ex)
{
throw ex;
}
catch (final Exception ex)
{
handleError(ex);
}
finally
{
isInCallback = false;
}
}
private void notifyCloseHandlers()
{
for (final Runnable closeHandler : closeHandlerByIdMap.values())
{
isInCallback = true;
try
{
closeHandler.run();
}
catch (final Exception ex)
{
handleError(ex);
}
finally
{
isInCallback = false;
}
}
}
}