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

org.elasticsearch.action.support.RefCountingRunnable Maven / Gradle / Ivy

There is a newer version: 8.15.1
Show newest version
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

package org.elasticsearch.action.support;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.util.concurrent.CountDown;
import org.elasticsearch.core.AbstractRefCounted;
import org.elasticsearch.core.RefCounted;
import org.elasticsearch.core.Releasable;
import org.elasticsearch.core.Releasables;

/**
 * A mechanism to trigger an action on the completion of some (dynamic) collection of other actions. Basic usage is as follows:
 *
 * 
 * try (var refs = new RefCountingRunnable(finalRunnable)) {
 *     for (var item : collection) {
 *         runAsyncAction(item, refs.acquire()); // releases the acquired ref on completion
 *     }
 * }
 * 
* * The delegate action is completed when execution leaves the try-with-resources block and every acquired reference is released. Unlike a * {@link CountDown} there is no need to declare the number of subsidiary actions up front (refs can be acquired dynamically as needed) nor * does the caller need to check for completion each time a reference is released. Moreover even outside the try-with-resources block you * can continue to acquire additional references, even in a separate thread, as long as there's at least one reference outstanding: * *
 * try (var refs = new RefCountingRunnable(finalRunnable)) {
 *     for (var item : collection) {
 *         if (condition(item)) {
 *             runAsyncAction(item, refs.acquire());
 *         }
 *     }
 *     if (flag) {
 *         runOneOffAsyncAction(refs.acquire());
 *         return;
 *     }
 *     for (var item : otherCollection) {
 *         var itemRef = refs.acquire(); // delays completion while the background action is pending
 *         executorService.execute(() -> {
 *             try (var ignored = itemRef) {
 *                 if (condition(item)) {
 *                     runOtherAsyncAction(item, refs.acquire());
 *                 }
 *             }
 *         });
 *     }
 * }
 * 
* * In particular (and also unlike a {@link CountDown}) this works even if you don't acquire any extra refs at all: in that case, the * delegate action executes at the end of the try-with-resources block. */ public final class RefCountingRunnable implements Releasable { private static final Logger logger = LogManager.getLogger(RefCountingRunnable.class); private final RefCounted refCounted; /** * Construct a {@link RefCountingRunnable} which executes {@code delegate} when all refs are released. * @param delegate The action to execute when all refs are released. This action must not throw any exception. */ public RefCountingRunnable(Runnable delegate) { this.refCounted = AbstractRefCounted.of(delegate); } /** * Acquire a reference to this object and return an action which releases it. The delegate {@link Runnable} is called when all its * references have been released. * * It is invalid to call this method once all references are released. Doing so will trip an assertion if assertions are enabled, and * will throw an {@link IllegalStateException} otherwise. * * It is also invalid to release the acquired resource more than once. Doing so will trip an assertion if assertions are enabled, but * will be ignored otherwise. This deviates from the contract of {@link java.io.Closeable}. */ public Releasable acquire() { refCounted.mustIncRef(); // All refs are considered equal so there's no real need to allocate a new object here, although note that this deviates (subtly) // from the docs for Closeable#close() which indicate that it should be idempotent. But only if assertions are disabled, and if // assertions are enabled then we are asserting that we never double-close these things anyway. return Releasables.assertOnce(this); } /** * Acquire a reference to this object and return a listener which releases it when notified. The delegate {@link Runnable} is called * when all its references have been released. */ public ActionListener acquireListener() { return ActionListener.releasing(acquire()); } /** * Release the original reference to this object, which executes the delegate {@link Runnable} if there are no other references. * * It is invalid to call this method more than once. Doing so will trip an assertion if assertions are enabled, but will be ignored * otherwise. This deviates from the contract of {@link java.io.Closeable}. */ @Override public void close() { try { refCounted.decRef(); } catch (Exception e) { logger.error("exception in delegate", e); assert false : e; } } @Override public String toString() { return refCounted.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy