/* * Quasar: lightweight threads and actors for the JVM. * Copyright (c) 2013-2015, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 3.0 * as published by the Free Software Foundation. */ package co.paralleluniverse.strands.channels.disruptor; import co.paralleluniverse.fibers.SuspendExecution; import co.paralleluniverse.strands.Timeout; import co.paralleluniverse.strands.channels.ReceivePort; import com.lmax.disruptor.AbstractSequencer; import com.lmax.disruptor.AlertException; import com.lmax.disruptor.RingBuffer; import com.lmax.disruptor.Sequence; import com.lmax.disruptor.Sequencer; import com.lmax.disruptor.WaitStrategy; import java.lang.reflect.Field; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * * @author pron */ public class DisruptorReceiveChannel<Message> implements ReceivePort<Message> { private final SequenceBarrier barrier; private final RingBuffer<Message> buffer; private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE); private long availableSequence = -1L; private volatile boolean closed; public DisruptorReceiveChannel(RingBuffer<Message> buffer, Sequence... dependentSequences) { this.buffer = buffer; final Sequencer sequencer = getSequencer(buffer); final WaitStrategy waitStrategy = getWaitStrategy(sequencer); final Sequence cursor = getCursor(sequencer); if (!(waitStrategy instanceof StrandBlockingWaitStrategy)) throw new IllegalArgumentException("Channel can only be created from RingBuffer with StrandBlockingWaitStrategy"); this.barrier = new ProcessingSequenceBarrier(sequencer, waitStrategy, cursor, dependentSequences); barrier.clearAlert(); } @Override public Message receive() throws SuspendExecution, InterruptedException { if (closed) return null; long nextSequence = sequence.get() + 1L; try { while (nextSequence > availableSequence) availableSequence = barrier.waitFor1(nextSequence); Message message = buffer.get(nextSequence); return message; } catch (AlertException ex) { return null; } } @Override public Message receive(long timeout, TimeUnit unit) throws SuspendExecution, InterruptedException { if (unit == null) return receive(); if (timeout <= 0) return tryReceive(); if (closed) return null; try { long nextSequence = sequence.get() + 1L; if (nextSequence > availableSequence) { final long start = System.nanoTime(); long left = unit.toNanos(timeout); final long deadline = start + unit.toNanos(timeout); while (nextSequence > availableSequence) availableSequence = barrier.waitFor1(nextSequence, left, TimeUnit.NANOSECONDS); if (nextSequence > availableSequence) { left = deadline - System.nanoTime(); if (left <= 0) return null; } } Message message = buffer.get(nextSequence); return message; } catch (TimeoutException | AlertException e) { return null; } } @Override public Message receive(Timeout timeout) throws SuspendExecution, InterruptedException { return receive(timeout.nanosLeft(), TimeUnit.NANOSECONDS); } @Override public Message tryReceive() { if (closed) return null; long nextSequence = sequence.get() + 1L; if (nextSequence > availableSequence) return null; return buffer.get(nextSequence); } private static Sequencer getSequencer(RingBuffer<?> buffer) { try { return (Sequencer) sequencerField.get(buffer); } catch (IllegalArgumentException ex) { throw new AssertionError(ex); } catch (IllegalAccessException ex) { throw new Error(ex); } } private static Sequence getCursor(Sequencer sequencer) { try { return (Sequence) cursorField.get(sequencer); } catch (IllegalArgumentException ex) { throw new AssertionError(ex); } catch (IllegalAccessException ex) { throw new Error(ex); } } private static WaitStrategy getWaitStrategy(Sequencer sequencer) { try { return (WaitStrategy) waitStrategyField.get(sequencer); } catch (IllegalArgumentException ex) { throw new AssertionError(ex); } catch (IllegalAccessException ex) { throw new Error(ex); } } @Override public void close() { this.closed = true; } @Override public boolean isClosed() { return closed; } private static final Field sequencerField; private static final Field cursorField; private static final Field waitStrategyField; static { try { sequencerField = RingBuffer.class.getSuperclass().getDeclaredField("sequencer"); sequencerField.setAccessible(true); cursorField = AbstractSequencer.class.getDeclaredField("cursor"); cursorField.setAccessible(true); waitStrategyField = AbstractSequencer.class.getDeclaredField("waitStrategy"); waitStrategyField.setAccessible(true); } catch (NoSuchFieldException ex) { throw new AssertionError(ex); } catch (SecurityException ex) { throw new Error(ex); } } }