private boolean isDrained() {
    long minSubscriberPosition = Long.MAX_VALUE;

    for (final ReadablePosition subscriberPosition : subscriberPositions) {
      minSubscriberPosition = Math.min(minSubscriberPosition, subscriberPosition.getVolatile());
    }

    return minSubscriberPosition >= rebuildPosition.get();
  }
  /** {@inheritDoc} */
  public void close() {
    hwmPosition.close();
    rebuildPosition.close();
    for (final ReadablePosition position : subscriberPositions) {
      position.close();
    }

    congestionControl.close();
    rawLog.close();
  }
  /**
   * Called from the {@link DriverConductor}.
   *
   * @param now in nanoseconds
   * @param statusMessageTimeout for sending of Status Messages.
   */
  void trackRebuild(final long now, final long statusMessageTimeout) {
    long minSubscriberPosition = Long.MAX_VALUE;
    long maxSubscriberPosition = Long.MIN_VALUE;

    for (final ReadablePosition subscriberPosition : subscriberPositions) {
      final long position = subscriberPosition.getVolatile();
      minSubscriberPosition = Math.min(minSubscriberPosition, position);
      maxSubscriberPosition = Math.max(maxSubscriberPosition, position);
    }

    final long rebuildPosition = Math.max(this.rebuildPosition.get(), maxSubscriberPosition);
    final long hwmPosition = this.hwmPosition.getVolatile();

    final long scanOutcome =
        lossDetector.scan(
            termBuffers[indexByPosition(rebuildPosition, positionBitsToShift)],
            rebuildPosition,
            hwmPosition,
            now,
            termLengthMask,
            positionBitsToShift,
            initialTermId);

    final int rebuildTermOffset = (int) rebuildPosition & termLengthMask;
    final long newRebuildPosition =
        (rebuildPosition - rebuildTermOffset) + rebuildOffset(scanOutcome);
    this.rebuildPosition.proposeMaxOrdered(newRebuildPosition);

    final long ccOutcome =
        congestionControl.onTrackRebuild(
            now,
            minSubscriberPosition,
            nextSmPosition,
            hwmPosition,
            rebuildPosition,
            newRebuildPosition,
            lossFound(scanOutcome));

    final int window = CongestionControlUtil.receiverWindowLength(ccOutcome);
    final long threshold = CongestionControlUtil.positionThreshold(window);

    if (CongestionControlUtil.shouldForceStatusMessage(ccOutcome)
        || (now > (lastStatusMessageTimestamp + statusMessageTimeout))
        || (minSubscriberPosition > (nextSmPosition + threshold))) {
      scheduleStatusMessage(now, minSubscriberPosition, window);
      cleanBufferTo(minSubscriberPosition - (termLengthMask + 1));
    }
  }
  @Before
  public void setUp() {
    when(publicationLimit.getVolatile()).thenReturn(2L * SEND_BUFFER_CAPACITY);
    when(logBuffers.termBuffers()).thenReturn(termBuffers);
    when(logBuffers.termLength()).thenReturn(TERM_MIN_LENGTH);
    when(logBuffers.metaDataBuffer()).thenReturn(logMetaDataBuffer);
    when(conductor.mainLock()).thenReturn(conductorLock);

    initialTermId(logMetaDataBuffer, TERM_ID_1);
    timeOfLastStatusMessage(logMetaDataBuffer, 0);

    for (int i = 0; i < PARTITION_COUNT; i++) {
      termBuffers[i] = new UnsafeBuffer(allocateDirect(TERM_MIN_LENGTH));
    }

    publication =
        new Publication(
            conductor,
            CHANNEL,
            STREAM_ID_1,
            SESSION_ID_1,
            publicationLimit,
            logBuffers,
            CORRELATION_ID);

    publication.incRef();

    initialiseTailWithTermId(logMetaDataBuffer, PARTITION_INDEX, TERM_ID_1);
  }
 /**
  * Remove a {@link ReadablePosition} for a subscriber that has been removed so it is not tracked
  * for flow control.
  *
  * @param subscriberPosition for the subscriber that has been removed.
  */
 void removeSubscriber(final ReadablePosition subscriberPosition) {
   subscriberPositions = ArrayUtil.remove(subscriberPositions, subscriberPosition);
   subscriberPosition.close();
 }
 @Test
 public void shouldReportThatPublicationHasNotBeenConnectedYet() {
   when(publicationLimit.getVolatile()).thenReturn(0L);
   when(conductor.isPublicationConnected(anyLong())).thenReturn(false);
   assertFalse(publication.isConnected());
 }