blob: 7d9b9c96b3a0ee22bc3a6a67175e8fefc00a2f96 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* 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.android.internal.listeners;
import android.annotation.Nullable;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
/**
* Interface (trait) for executing listener style operations on an executor.
*/
public interface ListenerExecutor {
/**
* An listener operation to perform.
*
* @param <TListener> listener type
*/
interface ListenerOperation<TListener> {
/**
* Performs the operation on the given listener.
*/
void operate(TListener listener) throws Exception;
/**
* Called before this operation is to be run. An operation may be canceled before it is run,
* in which case this method may not be invoked. {@link #onPostExecute(boolean)} will only
* be invoked if this method was previously invoked. This callback is invoked on the
* calling thread.
*/
default void onPreExecute() {}
/**
* Called if the operation fails while running. Will not be invoked in the event of a
* unchecked exception, which will propagate normally. This callback is invoked on the
* executor thread.
*/
default void onFailure(Exception e) {}
/**
* Called after the operation may have been run. Will always be invoked for every operation
* which has previously had {@link #onPreExecute()} invoked. Success implies that the
* operation was run to completion with no failures. If {@code success} is true, this
* callback will always be invoked on the executor thread. If {@code success} is false, this
* callback may be invoked on the calling thread or executor thread.
*/
default void onPostExecute(boolean success) {}
/**
* Will always be called once for every operation submitted to
* {@link #executeSafely(Executor, Supplier, ListenerOperation)}, no matter if the operation
* was run or not. This method is always invoked last, after every other callback. Success
* implies that the operation was run to completion with no failures. If {@code success}
* is true, this callback will always be invoked on the executor thread. If {@code success}
* is false, this callback may be invoked on the calling thread or executor thread.
*/
default void onComplete(boolean success) {}
}
/**
* An callback for listener operation failure.
*
* @param <TListenerOperation> listener operation type
*/
interface FailureCallback<TListenerOperation extends ListenerOperation<?>> {
/**
* Called if a listener operation fails while running with a checked exception. This
* callback is invoked on the executor thread.
*/
void onFailure(TListenerOperation operation, Exception exception);
}
/**
* See {@link #executeSafely(Executor, Supplier, ListenerOperation, FailureCallback)}.
*/
default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
@Nullable ListenerOperation<TListener> operation) {
executeSafely(executor, listenerSupplier, operation, null);
}
/**
* Executes the given listener operation on the given executor, using the provided listener
* supplier. If the supplier returns a null value, or a value during the operation that does not
* match the value prior to the operation, then the operation is considered canceled. If a null
* operation is supplied, nothing happens. If a failure callback is supplied, this will be
* invoked on the executor thread in the event a checked exception is thrown from the listener
* operation.
*/
default <TListener, TListenerOperation extends ListenerOperation<TListener>> void executeSafely(
Executor executor, Supplier<TListener> listenerSupplier,
@Nullable TListenerOperation operation,
@Nullable FailureCallback<TListenerOperation> failureCallback) {
if (operation == null) {
return;
}
TListener listener = listenerSupplier.get();
if (listener == null) {
return;
}
boolean executing = false;
boolean preexecute = false;
try {
operation.onPreExecute();
preexecute = true;
executor.execute(() -> {
boolean success = false;
try {
if (listener == listenerSupplier.get()) {
operation.operate(listener);
success = true;
}
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
operation.onFailure(e);
if (failureCallback != null) {
failureCallback.onFailure(operation, e);
}
}
} finally {
operation.onPostExecute(success);
operation.onComplete(success);
}
});
executing = true;
} finally {
if (!executing) {
if (preexecute) {
operation.onPostExecute(false);
}
operation.onComplete(false);
}
}
}
}