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

org.cometd.common.AsyncFoldLeft Maven / Gradle / Ivy

/*
 * Copyright (c) 2008 the original author or authors.
 *
 * 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 org.cometd.common;

import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.IntFunction;
import org.cometd.bayeux.Promise;

/**
 * 

Processes asynchronously a sequence of elements, producing a result that * is the reduction of the results produced by the processing of each element.

*

This class implements the asynchronous version of the following synchronous * {@code for} loop, but where the processing of each element may be asynchronous.

*
{@code
 * R result;
 * for (T element : list) {
 *     (result, proceed) = operation.apply(result, element);
 *     if (!proceed) {
 *         break;
 *     }
 * }
 * }
*

Using this class, the loop above becomes:

*
{@code
 * R zero;
 * AsyncFoldLeft.run(list, zero, (result, element, loop) -> {
 *     CompletableFuture future = processAsync(element);
 *     future.whenComplete((r, x) -> {
 *         if (x == null) {
 *             R reduced = reduce(result, r);
 *             if (shouldIterate(r)) {
 *                 loop.proceed(reduced);
 *             } else {
 *                 loop.leave(reduced);
 *             }
 *         } else {
 *             loop.fail(x);
 *         }
 *     })
 * }, Promise.complete((r, x) -> {
 *     // Process final result or failure.
 * });
 * }
*/ public class AsyncFoldLeft { /** *

Processes the given array of elements.

* * @param array the elements to process * @param zero the initial result * @param operation the operation to invoke for each element * @param promise the promise to notify of the final result * @param the type of element * @param the type of the result * @see #run(Iterable, Object, Operation, Promise) */ public static void run(T[] array, R zero, Operation operation, Promise promise) { int size = array.length; if (size == 0) { promise.succeed(zero); } else { IndexedLoop loop = new IndexedLoop<>(i -> array[i], size, zero, operation, promise); loop.run(); } } /** *

Processes the given sequence of elements.

*

The initial result {@code zero} is returned if the sequence is empty.

*

For each element the {@link Operation#apply(Object, Object, Loop) operation} * function is invoked.

*

The sequence should have a "stable" iterator, i.e. it should not be * affected if the sequence is modified during the iteration, either concurrently * by another thread, or by the same thread in the {@code operation} function.

* * @param iterable the elements to process * @param zero the initial result * @param operation the operation to invoke for each element * @param promise the promise to notify of the final result * @param the type of element * @param the type of the result */ public static void run(Iterable iterable, R zero, Operation operation, Promise promise) { Iterator iterator = iterable.iterator(); if (!iterator.hasNext()) { promise.succeed(zero); } else { IteratorLoop loop = new IteratorLoop<>(iterator, zero, operation, promise); loop.run(); } } /** *

Processes, in reverse order, the given sequence of elements.

* * @param list the elements to process * @param zero the initial result * @param operation the operation to invoke for each element * @param promise the promise to notify of the final result * @param the type of element * @param the type of the result * @see #run(Iterable, Object, Operation, Promise) */ public static void reverseRun(List list, R zero, Operation operation, Promise promise) { run(new ReverseIterable<>(list), zero, operation, promise); } private AsyncFoldLeft() { } /** *

The operation to invoke for each element.

* * @param the type of element * @param the type of the result */ @FunctionalInterface public interface Operation { /** *

Processes the given {@code element}.

*

The processing of the element must produce a result that is the reduction * of the accumulated {@code result} with the result of the processing of the * given {@code element}.

*

After the processing of the given element, the implementation must decide * whether to continue with or break out of the iteration (with the reduced * result) or to fail the iteration (with an exception).

* * @param result the accumulated result so far * @param element the element to process * @param loop the control object that continues or breaks the iteration */ void apply(R result, T element, Loop loop); } /** *

Controls the iteration over a sequence of elements, allowing to * continue the iteration (with a result), break out of the iteration * (with a result), or fail the iteration (with an exception).

* * @param the type of the result */ public interface Loop { /** *

Makes the loop proceed to the next element (or the end of the loop).

* * @param r the result computed in the current iteration */ public default void proceed(R r) { } /** *

Makes the loop exit (similarly to the {@code break} statement).

* * @param r the result computed in the current iteration */ public default void leave(R r) { } /** *

Makes the loop fail (similarly to throwing an exception).

* * @param failure the failure of the current iteration */ public default void fail(Throwable failure) { } } private enum State { LOOP, ASYNC, PROCEED, LEAVE, FAIL } private static abstract class AbstractLoop implements Loop { private final AtomicReference state = new AtomicReference<>(State.LOOP); private final AtomicReference failure = new AtomicReference<>(); private final AtomicReference result; private final Operation operation; private final Promise promise; private AbstractLoop(R zero, Operation operation, Promise promise) { this.result = new AtomicReference<>(zero); this.operation = operation; this.promise = promise; } abstract boolean hasCurrent(); abstract T current(); abstract void next(); void run() { while (hasCurrent()) { state.set(State.LOOP); operation.apply(result.get(), current(), this); loop: while (true) { State current = state.get(); switch (current) { case LOOP: if (state.compareAndSet(current, State.ASYNC)) { return; } break; case PROCEED: next(); break loop; case LEAVE: promise.succeed(result.get()); return; case FAIL: promise.fail(failure.get()); return; default: throw new IllegalStateException("Could not run loop in state " + current); } } } promise.succeed(result.get()); } @Override public void proceed(R r) { result.set(r); while (true) { State current = state.get(); switch (current) { case LOOP -> { if (state.compareAndSet(current, State.PROCEED)) { return; } } case ASYNC -> { if (state.compareAndSet(current, State.PROCEED)) { next(); run(); return; } } default -> throw new IllegalStateException("Could not proceed loop in state " + current); } } } @Override public void leave(R r) { result.set(r); while (true) { State current = state.get(); switch (current) { case LOOP -> { if (state.compareAndSet(current, State.LEAVE)) { return; } } case ASYNC -> { if (state.compareAndSet(current, State.LEAVE)) { promise.succeed(result.get()); return; } } default -> throw new IllegalStateException("Could not leave loop in state " + current); } } } @Override public void fail(Throwable x) { failure.compareAndSet(null, x); while (true) { State current = state.get(); switch (current) { case LOOP -> { if (state.compareAndSet(current, State.FAIL)) { return; } } case ASYNC -> { if (state.compareAndSet(current, State.FAIL)) { promise.fail(x); return; } } default -> throw new IllegalStateException("Could not fail loop in state " + current); } } } } private static class IndexedLoop extends AbstractLoop { private final IntFunction element; private final int size; private int index; private IndexedLoop(IntFunction element, int size, R zero, Operation operation, Promise promise) { super(zero, operation, promise); this.element = element; this.size = size; } @Override boolean hasCurrent() { return index < size; } @Override T current() { return element.apply(index); } @Override void next() { ++index; } } private static class IteratorLoop extends AbstractLoop { private final Iterator iterator; private boolean hasCurrent; private T current; private IteratorLoop(Iterator iterator, R zero, Operation operation, Promise promise) { super(zero, operation, promise); this.iterator = iterator; next(); } @Override boolean hasCurrent() { return hasCurrent; } @Override T current() { if (!hasCurrent) { throw new NoSuchElementException(); } return current; } @Override void next() { hasCurrent = iterator.hasNext(); current = hasCurrent ? iterator.next() : null; } } private static class ReverseIterable implements Iterable, Iterator { private final ListIterator iterator; private ReverseIterable(List list) { this.iterator = list.listIterator(list.size()); } @Override public Iterator iterator() { return this; } @Override public boolean hasNext() { return iterator.hasPrevious(); } @Override public T next() { return iterator.previous(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy