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: 7.10.2_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(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
     *        }
     *    }
     * 
    * * */ 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; } } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy