@Test public void shouldWaitForWorkCompleteWhereAllWorkersAreBlockedOnRingBuffer() throws Exception { long expectedNumberMessages = 10; fillRingBuffer(expectedNumberMessages); final StubEventProcessor[] workers = new StubEventProcessor[3]; for (int i = 0, size = workers.length; i < size; i++) { workers[i] = new StubEventProcessor(); workers[i].setSequence(expectedNumberMessages - 1); } final SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(Util.getSequencesFor(workers)); Runnable runnable = new Runnable() { public void run() { long sequence = ringBuffer.next(); StubEvent event = ringBuffer.getPreallocated(sequence); event.setValue((int) sequence); ringBuffer.publish(sequence); for (StubEventProcessor stubWorker : workers) { stubWorker.setSequence(sequence); } } }; new Thread(runnable).start(); long expectedWorkSequence = expectedNumberMessages; long completedWorkSequence = sequenceBarrier.waitFor(expectedNumberMessages); assertTrue(completedWorkSequence >= expectedWorkSequence); }
@Test public void shouldWaitForWorkCompleteWhereCompleteWorkThresholdIsAhead() throws Exception { final long expectedNumberMessages = 10; final long expectedWorkSequence = 9; fillRingBuffer(expectedNumberMessages); final Sequence sequence1 = new Sequence(expectedNumberMessages); final Sequence sequence2 = new Sequence(expectedWorkSequence); final Sequence sequence3 = new Sequence(expectedNumberMessages); context.checking( new Expectations() { { one(eventProcessor1).getSequence(); will(returnValue(sequence1)); one(eventProcessor2).getSequence(); will(returnValue(sequence2)); one(eventProcessor3).getSequence(); will(returnValue(sequence3)); } }); SequenceBarrier sequenceBarrier = ringBuffer.newBarrier( eventProcessor1.getSequence(), eventProcessor2.getSequence(), eventProcessor3.getSequence()); long completedWorkSequence = sequenceBarrier.waitFor(expectedWorkSequence); assertTrue(completedWorkSequence >= expectedWorkSequence); }
@Test public void shouldWaitForWorkCompleteWhereCompleteWorkThresholdIsBehind() throws Exception { long expectedNumberMessages = 10; fillRingBuffer(expectedNumberMessages); final StubEventProcessor[] eventProcessors = new StubEventProcessor[3]; for (int i = 0, size = eventProcessors.length; i < size; i++) { eventProcessors[i] = new StubEventProcessor(); eventProcessors[i].setSequence(expectedNumberMessages - 2); } final SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(Util.getSequencesFor(eventProcessors)); Runnable runnable = new Runnable() { public void run() { for (StubEventProcessor stubWorker : eventProcessors) { stubWorker.setSequence(stubWorker.getSequence().get() + 1L); } } }; Thread thread = new Thread(runnable); thread.start(); thread.join(); long expectedWorkSequence = expectedNumberMessages - 1; long completedWorkSequence = sequenceBarrier.waitFor(expectedWorkSequence); assertTrue(completedWorkSequence >= expectedWorkSequence); }
@Test public void shouldInterruptDuringBusySpin() throws Exception { final long expectedNumberMessages = 10; fillRingBuffer(expectedNumberMessages); final CountDownLatch latch = new CountDownLatch(3); final Sequence sequence1 = new CountDownLatchSequence(8L, latch); final Sequence sequence2 = new CountDownLatchSequence(8L, latch); final Sequence sequence3 = new CountDownLatchSequence(8L, latch); context.checking( new Expectations() { { one(eventProcessor1).getSequence(); will(returnValue(sequence1)); one(eventProcessor2).getSequence(); will(returnValue(sequence2)); one(eventProcessor3).getSequence(); will(returnValue(sequence3)); } }); final SequenceBarrier sequenceBarrier = ringBuffer.newBarrier( Util.getSequencesFor(eventProcessor1, eventProcessor2, eventProcessor3)); final boolean[] alerted = {false}; Thread t = new Thread( new Runnable() { public void run() { try { sequenceBarrier.waitFor(expectedNumberMessages - 1); } catch (AlertException e) { alerted[0] = true; } catch (InterruptedException e) { // don't care } } }); t.start(); latch.await(3, TimeUnit.SECONDS); sequenceBarrier.alert(); t.join(); assertTrue("Thread was not interrupted", alerted[0]); }
@Override public long waitOnLock( long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier) throws AlertException, InterruptedException { long availableSequence; lock.lock(); try { while ((availableSequence = cursorSequence.get()) < sequence) { barrier.checkAlert(); processorNotifyCondition.await(); } } finally { lock.unlock(); } while ((availableSequence = dependentSequence.get()) < sequence) { barrier.checkAlert(); } return availableSequence; }
/** * It is ok to have another thread rerun this method after a halt(). * * @throws IllegalStateException if this object instance is already running in a thread */ @Override public void run() { if (!running.compareAndSet(false, true)) { throw new IllegalStateException("Thread is already running"); } sequenceBarrier.clearAlert(); notifyStart(); T event = null; long nextSequence = sequence.get() + 1L; while (true) { try { final long availableSequence = sequenceBarrier.waitFor(nextSequence); while (nextSequence <= availableSequence) { event = dataProvider.get(nextSequence); eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence); nextSequence++; } sequence.set(availableSequence); } catch (final TimeoutException e) { notifyTimeout(sequence.get()); } catch (final AlertException ex) { if (!running.get()) { break; } } catch (final Throwable ex) { exceptionHandler.handleEventException(ex, nextSequence, event); sequence.set(nextSequence); nextSequence++; } } notifyShutdown(); running.set(false); }
@Test public void shouldSetAndClearAlertStatus() { SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(); assertFalse(sequenceBarrier.isAlerted()); sequenceBarrier.alert(); assertTrue(sequenceBarrier.isAlerted()); sequenceBarrier.clearAlert(); assertFalse(sequenceBarrier.isAlerted()); }
@Override public void halt() { running.set(false); sequenceBarrier.alert(); }