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

com.hazelcast.util.executor.CachedExecutorServiceDelegate Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed 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 com.hazelcast.util.executor;

import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.util.EmptyStatement;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static java.util.concurrent.atomic.AtomicLongFieldUpdater.newUpdater;

public final class CachedExecutorServiceDelegate implements ExecutorService, ManagedExecutorService {

    public static final long TIME = 250;

    private static final AtomicLongFieldUpdater EXECUTED_COUNT =
            newUpdater(CachedExecutorServiceDelegate.class, "executedCount");

    private volatile long executedCount;
    private final String name;
    private final int maxPoolSize;
    private final ExecutorService cachedExecutor;
    private final NodeEngine nodeEngine;
    private final BlockingQueue taskQ;
    private final Lock lock = new ReentrantLock();
    private volatile int size;

    public CachedExecutorServiceDelegate(NodeEngine nodeEngine, String name, ExecutorService cachedExecutor,
                                         int maxPoolSize, int queueCapacity) {
        if (maxPoolSize <= 0) {
            throw new IllegalArgumentException("Max pool size must be positive!");
        }
        if (queueCapacity <= 0) {
            throw new IllegalArgumentException("Queue capacity must be positive!");
        }
        this.name = name;
        this.maxPoolSize = maxPoolSize;
        this.cachedExecutor = cachedExecutor;
        this.taskQ = new LinkedBlockingQueue(queueCapacity);
        this.nodeEngine = nodeEngine;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public long getCompletedTaskCount() {
        return executedCount;
    }

    @Override
    public int getMaximumPoolSize() {
        return maxPoolSize;
    }

    @Override
    public int getPoolSize() {
        return size;
    }

    @Override
    public int getQueueSize() {
        // LBQ size handled by an atomic int
        return taskQ.size();
    }

    @Override
    public int getRemainingQueueCapacity() {
        return taskQ.remainingCapacity();
    }

    @Override
    public void execute(Runnable command) {
        if (!taskQ.offer(command)) {
            throw new RejectedExecutionException("Executor[" + name + "] is overloaded!");
        }
        addNewWorkerIfRequired();
    }

    @Override
    public  Future submit(Callable task) {
        final RunnableFuture rf = new CompletableFutureTask(task, getAsyncExecutor());
        execute(rf);
        return rf;
    }

    @Override
    public  Future submit(Runnable task, T result) {
        final RunnableFuture rf = new CompletableFutureTask(task, result, getAsyncExecutor());
        execute(rf);
        return rf;
    }

    @Override
    public Future submit(Runnable task) {
        return submit(task, null);
    }

    @SuppressFBWarnings("VO_VOLATILE_INCREMENT")
    private void addNewWorkerIfRequired() {
        if (size < maxPoolSize) {
            try {
                if (lock.tryLock(TIME, TimeUnit.MILLISECONDS)) {
                    try {
                        if (size < maxPoolSize && getQueueSize() > 0) {
                            size++;
                            cachedExecutor.execute(new Worker());
                        }
                    } finally {
                        lock.unlock();
                    }
                }
            } catch (InterruptedException ignored) {
                EmptyStatement.ignore(ignored);
            }
        }
    }

    @Override
    public void shutdown() {
        taskQ.clear();
    }

    @Override
    public List shutdownNow() {
        shutdown();
        return null;
    }

    @Override
    public boolean isShutdown() {
        return false;
    }

    @Override
    public boolean isTerminated() {
        return false;
    }

    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public  List> invokeAll(Collection> tasks) throws InterruptedException {
        throw new UnsupportedOperationException();
    }

    @Override
    public  List> invokeAll(Collection> tasks,
                                         long timeout, TimeUnit unit) throws InterruptedException {
        throw new UnsupportedOperationException();
    }

    @Override
    public  T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException {
        throw new UnsupportedOperationException();
    }

    @Override
    public  T invokeAny(Collection> tasks, long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
        throw new UnsupportedOperationException();
    }

    private ExecutorService getAsyncExecutor() {
        return nodeEngine.getExecutionService().getExecutor(ExecutionService.ASYNC_EXECUTOR);
    }

    private class Worker implements Runnable {

        @Override
        public void run() {
            try {
                Runnable r;
                do {
                    r = taskQ.poll(1, TimeUnit.MILLISECONDS);
                    if (r != null) {
                        r.run();
                        EXECUTED_COUNT.incrementAndGet(CachedExecutorServiceDelegate.this);
                    }
                }
                while (r != null);
            } catch (InterruptedException ignored) {
                EmptyStatement.ignore(ignored);
            } finally {
                exit();
            }
        }

        void exit() {
            lock.lock();
            try {
                size--;
                if (taskQ.peek() != null) {
                    // may cause underlying cached executor to create some extra threads!
                    addNewWorkerIfRequired();
                }
            } finally {
                lock.unlock();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy