/** * Copyright 2014 Ricardo Padilha * * 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 net.dsys.snio.impl.buffer; import javax.annotation.Nonnull; import net.dsys.snio.api.buffer.InterruptedByClose; import net.dsys.snio.api.buffer.MessageBufferConsumer; import com.lmax.disruptor.AlertException; import com.lmax.disruptor.RingBuffer; import com.lmax.disruptor.Sequence; import com.lmax.disruptor.SequenceBarrier; import com.lmax.disruptor.TimeoutException; /** * @author Ricardo Padilha */ final class RingBufferConsumer<T> implements MessageBufferConsumer<T> { private final RingBuffer<T> buffer; private final Object[] attachments; private final SequenceBarrier barrier; private final Sequence sequence; private long cursor; private long available; private boolean closed; RingBufferConsumer(@Nonnull final RingBuffer<T> buffer, @Nonnull final Object[] attachments) { if (buffer == null) { throw new NullPointerException("buffer == null"); } if (attachments == null) { throw new NullPointerException("attachments == null"); } if (buffer.getBufferSize() != attachments.length) { throw new IllegalArgumentException("buffer.getBufferSize() != attachments.length"); } this.buffer = buffer; this.attachments = attachments; this.barrier = buffer.newBarrier(); this.sequence = new Sequence(); buffer.addGatingSequences(sequence); this.cursor = sequence.get(); this.available = sequence.get(); } /** * {@inheritDoc} */ @Override public RingBufferProducer<T> createProducer() { return new RingBufferProducer<>(buffer, attachments); } void close() { closed = true; buffer.removeGatingSequence(sequence); barrier.alert(); } /** * {@inheritDoc} */ @Override public long acquire() throws InterruptedException { if (closed) { throw new InterruptedByClose(); } final long newCursor = cursor + 1; if (newCursor <= available) { return newCursor; } try { do { available = barrier.waitFor(newCursor); } while (available < newCursor); } catch (final AlertException e) { throw new InterruptedByClose(); } catch (final TimeoutException e) { throw new InterruptedException(e.getLocalizedMessage()); } return newCursor; } /** * {@inheritDoc} */ @Override public long acquire(final int n) throws InterruptedException { if (closed) { throw new InterruptedByClose(); } final long newCursor = cursor + n; if (newCursor <= available) { return newCursor; } try { final long minCursor = cursor + 1; do { available = barrier.waitFor(newCursor); } while (available < minCursor); } catch (final AlertException e) { throw new InterruptedByClose(); } catch (final TimeoutException e) { throw new InterruptedException(e.getLocalizedMessage()); } return Math.min(newCursor, available); } /** * {@inheritDoc} */ @Override public int remaining() { long c = buffer.getCursor(); while (!buffer.isPublished(c)) { c--; } available = c; return (int) (available - cursor); } /** * {@inheritDoc} */ @Override public T get(final long sequence) { return buffer.get(sequence); } /** * {@inheritDoc} */ @Override public Object attachment(final long sequence) { return attachments[(int) (sequence % attachments.length)]; } /** * {@inheritDoc} */ @Override public void release(final long seq) throws InterruptedException { if (closed) { throw new InterruptedByClose(); } if (seq > available) { throw new IllegalArgumentException("seq > available"); } cursor = seq; if (cursor == available) { sequence.set(cursor); } } }