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

org.jgroups.util.AsyncExecutor Maven / Gradle / Ivy

There is a newer version: 5.4.1.Final
Show newest version
package org.jgroups.util;

import org.jgroups.Lifecycle;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.Property;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Supplier;

import static org.jgroups.conf.AttributeType.SCALAR;

/**
 * Used to execute asynchronous tasks, e.g. async-send (https://issues.redhat.com/browse/JGRP-2603). Uses a blockng
 * queue and a dequeuer thread, which passes removed tasks to the thread pool
 * @author Bela Ban
 * @since  5.3.5
 */
public class AsyncExecutor implements Lifecycle {

    @Property(description="If not enabled, tasks will executed on the runner's thread")
    protected boolean         enabled=true;

    @ManagedAttribute(description="Total number of times a message was sent (includes rejected messages)",type=SCALAR)
    protected final LongAdder num_sends=new LongAdder();

    @ManagedAttribute(description="Number of rejected message due to an exhausted thread pool (includes dropped " +
      "messages and messages sent on the caller's thread",type=SCALAR)
    protected final LongAdder num_rejected=new LongAdder();

    @ManagedAttribute(description="Number of dropped tasks (when DONT_BLOCK flag is set in the message)",type=SCALAR)
    protected final LongAdder num_drops_on_full_thread_pool=new LongAdder();

    @ManagedAttribute(description="Messages that were sent on the caller's thread due to an exhausted pool",type=SCALAR)
    protected final LongAdder num_sends_on_callers_thread=new LongAdder();

    protected ThreadPool      thread_pool;
    protected Executor        executor;

    public boolean          enabled()                  {return enabled;}
    public AsyncExecutor enable(boolean b)          {enabled=b; return this;}
    public ThreadPool       threadPool()               {return thread_pool;}
    public AsyncExecutor threadPool(ThreadPool p)   {this.thread_pool=p; return this;}
    public long             numSends()                 {return num_sends.sum();}
    public long             numSendsOnCallersThread()  {return num_sends_on_callers_thread.sum();}
    public long             numDropsOnFullThreadPool() {return num_drops_on_full_thread_pool.sum();}
    public long             numRejected()              {return num_rejected.sum();}


    public AsyncExecutor() {
    }

    public AsyncExecutor(ThreadPool p) {
        this.thread_pool=p;
    }

    public void resetStats() {
        num_sends.reset();
        num_rejected.reset();
        num_drops_on_full_thread_pool.reset();
        num_sends_on_callers_thread.reset();
    }

    public CompletableFuture execute(Supplier t, boolean can_be_dropped) {
        Task task=new Task<>(t, new CompletableFuture<>());
        Executor exec=executor;
        try {
            num_sends.increment();
            if(enabled && (exec=exec()) != null)
                return CompletableFuture.supplyAsync(t, exec);
            return CompletableFuture.completedFuture(t.get());
        }
        catch(RejectedExecutionException ex) {
            num_rejected.increment();
            if(!can_be_dropped) {
                task.run(); // if we cannot drop the task, run it on the caller thread
                num_sends_on_callers_thread.increment();
            }
            else {
                task.completeExceptionally(ex);
                num_drops_on_full_thread_pool.increment();
            }
        }
        return task.cf;
    }

    @Override
    public String toString() {
        return String.format("rejected: %,d, drops=%,d, sends_on_caller: %,d, pool: %s\n",
                             num_rejected.sum(), num_drops_on_full_thread_pool.sum(), num_sends_on_callers_thread.sum(),
                             thread_pool.toString());
    }

    protected Executor exec() {
        Executor exec=executor;
        return exec != null? exec : (exec=executor=thread_pool.pool());
    }


    protected static class Task implements Runnable {
        protected final Supplier        task;
        protected final CompletableFuture cf;

        protected Task(Supplier task, CompletableFuture cf) {
            this.task=task;
            this.cf=cf;
        }

        protected Task completeExceptionally(Throwable t) {
            cf.completeExceptionally(t);
            return this;
        }

        @Override
        public void run() {
            try {
                T result=task.get();
                cf.complete(result);
            }
            catch(Throwable t) {
                cf.completeExceptionally(t);
            }
        }
    }



}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy