org.elasticsearch.common.util.concurrent.ThreadBarrier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search licenses this
* file to you 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 org.elasticsearch.common.util.concurrent;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A synchronization aid that allows a set of threads to all wait for each other
* to reach a common barrier point. Barriers are useful in programs involving a
* fixed sized party of threads that must occasionally wait for each other.
* ThreadBarrier
adds a cause to
* {@link BrokenBarrierException} thrown by a {@link #reset()} operation defined
* by {@link CyclicBarrier}.
*
*
* Sample usage:
* Barrier as a synchronization and Exception handling aid
* Barrier as a trigger for elapsed notification events
*
*
* class MyTestClass implements RemoteEventListener
* {
* final ThreadBarrier barrier;
*
* class Worker implements Runnable
* {
* public void run()
* {
* barrier.await(); //wait for all threads to reach run
* try
* {
* prepare();
* barrier.await(); //wait for all threads to prepare
* process();
* barrier.await(); //wait for all threads to process
* }
* catch(Throwable t){
* log("Worker thread caught exception", t);
* barrier.reset(t);
* }
* }
* }
*
* public void testThreads() {
* barrier = new ThreadBarrier(N_THREADS + 1);
* for (int i = 0; i < N; ++i)
* new Thread(new Worker()).start();
*
* try{
* barrier.await(); //wait for all threads to reach run
* barrier.await(); //wait for all threads to prepare
* barrier.await(); //wait for all threads to process
* }
* catch(BrokenBarrierException bbe) {
* Assert.fail(bbe);
* }
* }
*
* int actualNotificationCount = 0;
* public synchronized void notify (RemoteEvent event) {
* try{
* actualNotificationCount++;
* if (actualNotificationCount == EXPECTED_COUNT)
* barrier.await(); //signal when all notifications arrive
*
* // too many notifications?
* Assert.assertFalse("Exceeded notification count",
* actualNotificationCount > EXPECTED_COUNT);
* }
* catch(Throwable t) {
* log("Worker thread caught exception", t);
* barrier.reset(t);
* }
* }
*
* public void testNotify() {
* barrier = new ThreadBarrier(N_LISTENERS + 1);
* registerNotification();
* triggerNotifications();
*
* //wait until either all notifications arrive, or
* //until a MAX_TIMEOUT is reached.
* barrier.await(MAX_TIMEOUT);
*
* //check if all notifications were accounted for or timed-out
* Assert.assertEquals("Notification count",
* EXPECTED_COUNT, actualNotificationCount);
*
* //inspect that the barrier isn't broken
* barrier.inspect(); //throws BrokenBarrierException if broken
* }
* }
*
*
* @author kimchy (shay.banon)
*/
public class ThreadBarrier extends CyclicBarrier {
/**
* The cause of a {@link BrokenBarrierException} and {@link TimeoutException}
* thrown from an await() when {@link #reset(Throwable)} was invoked.
*/
private Throwable cause;
public ThreadBarrier(int parties) {
super(parties);
}
public ThreadBarrier(int parties, Runnable barrierAction) {
super(parties, barrierAction);
}
@Override
public int await() throws InterruptedException, BrokenBarrierException {
try {
breakIfBroken();
return super.await();
} catch (BrokenBarrierException bbe) {
initCause(bbe);
throw bbe;
}
}
@Override public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException {
try {
breakIfBroken();
return super.await(timeout, unit);
} catch (BrokenBarrierException bbe) {
initCause(bbe);
throw bbe;
} catch (TimeoutException te) {
initCause(te);
throw te;
}
}
/**
* Resets the barrier to its initial state. If any parties are
* currently waiting at the barrier, they will return with a
* {@link BrokenBarrierException}. Note that resets after
* a breakage has occurred for other reasons can be complicated to
* carry out; threads need to re-synchronize in some other way,
* and choose one to perform the reset. It may be preferable to
* instead create a new barrier for subsequent use.
*
* @param cause The cause of the BrokenBarrierException
*/
public synchronized void reset(Throwable cause) {
if (!isBroken()) {
super.reset();
}
if (this.cause == null) {
this.cause = cause;
}
}
/**
* Queries if this barrier is in a broken state. Note that if
* {@link #reset(Throwable)} is invoked the barrier will remain broken, while
* {@link #reset()} will reset the barrier to its initial state and
* {@link #isBroken()} will return false.
*
* @return {@code true} if one or more parties broke out of this barrier due
* to interruption or timeout since construction or the last reset,
* or a barrier action failed due to an exception; {@code false}
* otherwise.
* @see #inspect()
*/
@Override
public synchronized boolean isBroken() {
return this.cause != null || super.isBroken();
}
/**
* Inspects if the barrier is broken. If for any reason, the barrier
* was broken, a {@link BrokenBarrierException} will be thrown. Otherwise,
* would return gracefully.
*
* @throws BrokenBarrierException With a nested broken cause.
*/
public synchronized void inspect() throws BrokenBarrierException {
try {
breakIfBroken();
} catch (BrokenBarrierException bbe) {
initCause(bbe);
throw bbe;
}
}
/**
* breaks this barrier if it has been reset or broken for any other reason.
*
* Note: This call is not atomic in respect to await/reset calls. A
* breakIfBroken() may be context switched to invoke a reset() prior to
* await(). This resets the barrier to its initial state - parties not
* currently waiting at the barrier will not be accounted for! An await that
* wasn't time limited, will block indefinitely.
*
* @throws BrokenBarrierException an empty BrokenBarrierException.
*/
private synchronized void breakIfBroken()
throws BrokenBarrierException {
if (isBroken()) {
throw new BrokenBarrierException();
}
}
/**
* Initializes the cause of this throwable to the specified value. The cause
* is the throwable that was initialized by {@link #reset(Throwable)}.
*
* @param t throwable.
*/
private synchronized void initCause(Throwable t) {
t.initCause(this.cause);
}
/**
* A Barrier action to be used in conjunction with {@link ThreadBarrier} to
* measure performance between barrier awaits. This runnable will execute
* when the barrier is tripped. Make sure to reset() the timer before next
* Measurement.
*
* @see ThreadBarrier#ThreadBarrier(int, Runnable)
*
* Usage example:
*
* BarrierTimer timer = new BarrierTimer();
* ThreadBarrier barrier = new ThreadBarrier( nTHREADS + 1, timer );
* ..
* barrier.await(); // starts timer when all threads trip on await
* barrier.await(); // stops timer when all threads trip on await
* ..
* long time = timer.getTimeInNanos();
* long tpi = time / ((long)nREPEATS * nTHREADS); //throughput per thread iteration
* long secs = timer.getTimeInSeconds(); //total runtime in seconds
* ..
* timer.reset(); // reuse timer
*
*/
public static class BarrierTimer implements Runnable {
volatile boolean started;
volatile long startTime;
volatile long endTime;
public void run() {
long t = System.nanoTime();
if (!started) {
started = true;
startTime = t;
} else
endTime = t;
}
/**
* resets (clears) this timer before next execution.
*/
public void reset() {
started = false;
}
/**
* Returns the elapsed time between two successive barrier executions.
*
* @return elapsed time in nanoseconds.
*/
public long getTimeInNanos() {
return endTime - startTime;
}
/**
* Returns the elapsed time between two successive barrier executions.
*
* @return elapsed time in seconds.
*/
public double getTimeInSeconds() {
long time = endTime - startTime;
return (time) / 1000000000.0;
}
}
}