/* * Copyright 2015, 2016 Tagir Valeev * * 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 one.util.streamex; import java.util.concurrent.ForkJoinPool; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.BaseStream; import static one.util.streamex.StreamExInternals.*; /** * This class controls stream execution mode (parallel/sequential), custom FJP * and close handlers. * * Fields are package-private and mutable, but it's forbidden to change them * from outside of this class. * * For performance reasons shared objects SEQUENTIAL and PARALLEL are used: then * have no custom FJP and no close handler. If custom FJP or close handler is * requested for shared object, a new object is created, otherwise the current * one is modified. * * @author Tagir Valeev */ /* package */class StreamContext { static final StreamContext SEQUENTIAL = new StreamContext(false); static final StreamContext PARALLEL = new StreamContext(true); boolean parallel; ForkJoinPool fjp; Runnable closeHandler; private StreamContext(boolean parallel) { this.parallel = parallel; } <T> T terminate(Supplier<T> terminalOperation) { return fjp.submit(terminalOperation::get).join(); } <T, U> T terminate(U value, Function<U, T> terminalOperation) { return fjp.submit(() -> terminalOperation.apply(value)).join(); } StreamContext parallel() { if (this == SEQUENTIAL) return PARALLEL; this.parallel = true; this.fjp = null; return this; } StreamContext sequential() { if (this == PARALLEL) return SEQUENTIAL; this.parallel = false; this.fjp = null; return this; } StreamContext parallel(ForkJoinPool fjp) { StreamContext context = detach(); context.parallel = true; context.fjp = fjp; return context; } StreamContext detach() { if (this == PARALLEL || this == SEQUENTIAL) return new StreamContext(parallel); return this; } StreamContext onClose(Runnable r) { StreamContext context = detach(); context.closeHandler = compose(context.closeHandler, r); return context; } void close() { if (closeHandler != null) { Runnable r = closeHandler; closeHandler = null; r.run(); } } static Runnable compose(Runnable r1, Runnable r2) { if(r1 == null) return r2; return () -> { try { r1.run(); } catch (Throwable t1) { try { r2.run(); } catch (Throwable t2) { t1.addSuppressed(t2); } throw t1; } r2.run(); }; } StreamContext combine(BaseStream<?, ?> other) { if (other == null) return this; StreamContext otherStrategy = of(other); StreamContext result = this; if (other.isParallel() && !parallel) result = parallel(); if (otherStrategy.closeHandler != null) result = result.onClose(otherStrategy.closeHandler); return result; } static StreamContext of(BaseStream<?, ?> stream) { if (stream instanceof BaseStreamEx) return ((BaseStreamEx<?, ?, ?, ?>) stream).context; if (mustCloseStream(stream)) return new StreamContext(stream.isParallel()).onClose(stream::close); return stream.isParallel() ? PARALLEL : SEQUENTIAL; } }