All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.common.util.concurrent.ThreadBarrier Maven / Gradle / Ivy

There is a newer version: 8.14.1
Show newest version
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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(Exception e){
 *                  log("Worker thread caught exception", e);
 *                  barrier.reset(e);
 *                }
 *            }
 *        }
 *
 *      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(Exception e) {
 *              log("Worker thread caught exception", e);
 *              barrier.reset(e);
 *            }
 *        }
 *
 *      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
 *        }
 *    }
 * 
* * */ public class ThreadBarrier extends CyclicBarrier { /** * The cause of a {@link BrokenBarrierException} and {@link TimeoutException} * thrown from an await() when {@link #reset(Exception)} was invoked. */ private Exception 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(Exception 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(Exception)} 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(Exception)}. * * @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; @Override 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; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy