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

com.hazelcast.spi.impl.AbstractCompletableFuture Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2008-2017, 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.spi.impl;

import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.core.ICompletableFuture;
import com.hazelcast.logging.ILogger;
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.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

import static com.hazelcast.util.ExceptionUtil.sneakyThrow;
import static com.hazelcast.util.Preconditions.isNotNull;
import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;

/**
 * A base {@link ICompletableFuture} implementation that may be explicitly completed by setting its
 * value through setResult.
 * Implements the logic of cancellation and callbacks execution.
 *
 * @param  The result type returned by this Future's {@code get} method
 */
@SuppressFBWarnings(value = "NN_NAKED_NOTIFY", justification = "State handled with CAS, naked notify correct")
public abstract class AbstractCompletableFuture implements ICompletableFuture {

    private static final Object INITIAL_STATE = new ExecutionCallbackNode(null, null, null);
    private static final Object CANCELLED_STATE = new Object();

    private static final AtomicReferenceFieldUpdater STATE
            = newUpdater(AbstractCompletableFuture.class, Object.class, "state");

    // This field is only assigned by the STATE AtomicReferenceFieldUpdater.
    // This field encodes an option type of 2 types, an ExecutionCallbackNode or a result.
    // if the state is an instance of the former, then the future is not done. Otherwise it is.
    // The reason for this abuse of the type system is to deal with the head node and the result
    // in a single cas. Using a single cas prevent a thread that calls and then do this concurrently
    // with a thread that calls setResult.
    private volatile Object state = INITIAL_STATE;

    private final ILogger logger;
    private final Executor defaultExecutor;

    protected AbstractCompletableFuture(NodeEngine nodeEngine, ILogger logger) {
        this(nodeEngine.getExecutionService().getExecutor(ExecutionService.ASYNC_EXECUTOR), logger);
    }

    protected AbstractCompletableFuture(Executor defaultExecutor, ILogger logger) {
        this.defaultExecutor = defaultExecutor;
        this.logger = logger;
    }

    @Override
    public void andThen(ExecutionCallback callback) {
        andThen(callback, defaultExecutor);
    }

    @Override
    public void andThen(ExecutionCallback callback, Executor executor) {
        isNotNull(callback, "callback");
        isNotNull(executor, "executor");

        for (; ; ) {
            Object currentState = this.state;

            if (isCancelledState(currentState)) {
                return;
            }

            if (isDoneState(currentState)) {
                runAsynchronous(callback, executor, currentState);
                return;
            }

            ExecutionCallbackNode newState
                    = new ExecutionCallbackNode(callback, executor, (ExecutionCallbackNode) currentState);

            if (STATE.compareAndSet(this, currentState, newState)) {
                // we have successfully scheduled the callback.
                return;
            }

            // we failed to update the state. This can mean 2 things:
            // either a result was set, which we'll see when retrying this loop
            // or a different thread also called andThen, which we'll deal with when we retry the loop.
        }
    }

    @Override
    public boolean isDone() {
        return isDoneState(state);
    }

    /**
     * Returns {@code true} if the task with the given state completed - analogously to the Future's contract.
     * Completion may be due to normal termination, an exception, or
     * cancellation -- in all of these cases, this method will return
     * {@code true}.
     *
     * @return {@code true} if this task completed
     */
    private static boolean isDoneState(Object state) {
        return !(state instanceof ExecutionCallbackNode);
    }

    @Override
    public final boolean cancel(boolean mayInterruptIfRunning) {
        Boolean shouldCancel = null;

        for (; ; ) {
            Object currentState = this.state;

            if (isDoneState(currentState)) {
                return false;
            }

            if (shouldCancel == null) {
                shouldCancel = shouldCancel(mayInterruptIfRunning);
            }
            if (!shouldCancel) {
                return false;
            }

            if (STATE.compareAndSet(this, currentState, CANCELLED_STATE)) {
                cancelled(mayInterruptIfRunning);
                notifyThreadsWaitingOnGet();
                return true;
            }
        }
    }

    /**
     * Protected method invoked on cancel(). Enables aborting the cancellation.
     * Useful for futures' encompassing logic that can forbid the cancellation.
     * By default always returns true.
     *
     * @param mayInterruptIfRunning passed through from cancel call
     * @return true should the cancellation proceed; false otherwise
     */
    protected boolean shouldCancel(boolean mayInterruptIfRunning) {
        return true;
    }

    /**
     * Protected method invoked when this task transitions to state
     * {@code isCancelled}. The default implementation does nothing.
     * Subclasses may override this method to invoke callbacks or perform
     * bookkeeping. Implementation has to handle exceptions itself.
     *
     * @param mayInterruptIfRunning {@code true} if the thread executing this
     *                              task was supposed to be interrupted; otherwise, in-progress tasks are allowed
     *                              to complete
     */
    protected void cancelled(boolean mayInterruptIfRunning) {
    }

    private static boolean isCancelledState(Object state) {
        return state == CANCELLED_STATE;
    }

    @Override
    public boolean isCancelled() {
        return isCancelledState(state);
    }

    @Override
    public final V get() throws InterruptedException, ExecutionException {
        for (; ; ) {
            try {
                return get(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
            } catch (TimeoutException ignored) {
                // A timeout here can only be a spurious artifact.
                // It should never happen and even if it does, we must retry.
                EmptyStatement.ignore(ignored);
            }
        }
    }


    /**
     * PLEASE NOTE: It's legal to override this method, but please bear in mind that you should call super.get() or
     * implement the done() and cancelled() callbacks to be notified if this future gets done or cancelled.
     * Otherwise the overridden implementation of get() may get stuck on waiting forever.
     */
    @Override
    public V get(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
        final long deadlineTimeMillis = System.currentTimeMillis() + unit.toMillis(timeout);
        long millisToWait;
        for (; ; ) {
            Object currentState = this.state;

            if (isCancelledState(currentState)) {
                throw new CancellationException();
            }
            if (isDoneState(currentState)) {
                return getResult(currentState);
            }
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }

            millisToWait = deadlineTimeMillis - System.currentTimeMillis();
            if (millisToWait <= 0) {
                throw new TimeoutException();
            }

            synchronized (this) {
                if (!isDoneState(this.state)) {
                    wait(millisToWait);
                }
            }
        }
    }

    protected void setResult(Object result) {
        for (; ; ) {
            Object currentState = this.state;

            if (isDoneState(currentState)) {
                return;
            }

            if (STATE.compareAndSet(this, currentState, result)) {
                done();
                notifyThreadsWaitingOnGet();
                runAsynchronous((ExecutionCallbackNode) currentState, result);
                break;
            }
        }
    }

    /**
     * Protected method invoked when this task transitions to state
     * {@code isDone} (only normally - in case of cancellation cancelled() is invoked)
     * The default implementation does nothing.
     * Subclasses may override this method to invoke callbacks or perform
     * bookkeeping. Implementation has to handle exceptions itself.
     */
    protected void done() {
    }

    /**
     * Returns:
     * 
    *
  • null - if cancelled or not done
  • *
  • result - if done and result is NOT Throwable
  • *
  • sneaky throws an exception - if done and result is Throwable
  • *
*/ protected V getResult() { return getResult(this.state); } private static V getResult(Object state) { if (isCancelledState(state)) { return null; } if (!isDoneState(state)) { return null; } if (state instanceof Throwable) { sneakyThrow((Throwable) state); } return (V) state; } private void notifyThreadsWaitingOnGet() { synchronized (this) { notifyAll(); } } private void runAsynchronous(ExecutionCallbackNode head, Object result) { while (head != INITIAL_STATE) { runAsynchronous(head.callback, head.executor, result); head = head.next; } } private void runAsynchronous(ExecutionCallback callback, Executor executor, Object result) { executor.execute(new ExecutionCallbackRunnable(getClass(), result, callback, logger)); } private static final class ExecutionCallbackNode { final ExecutionCallback callback; final Executor executor; final ExecutionCallbackNode next; private ExecutionCallbackNode(ExecutionCallback callback, Executor executor, ExecutionCallbackNode next) { this.callback = callback; this.executor = executor; this.next = next; } } private static final class ExecutionCallbackRunnable implements Runnable { private final Class caller; private final Object result; private final ExecutionCallback callback; private final ILogger logger; public ExecutionCallbackRunnable(Class caller, Object result, ExecutionCallback callback, ILogger logger) { this.caller = caller; this.result = result; this.callback = callback; this.logger = logger; } @Override public void run() { try { if (result instanceof Throwable) { callback.onFailure((Throwable) result); } else { callback.onResponse((V) result); } } catch (Throwable cause) { logger.severe("Failed asynchronous execution of execution callback: " + callback + "for call " + caller, cause); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy