org.spf4j.base.ExecutionContexts Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spf4j-core Show documentation
Show all versions of spf4j-core Show documentation
A continuously growing collection of utilities to measure performance, get better diagnostics,
improve performance, or do things more reliably, faster that other open source libraries...
/*
* Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Additionally licensed with:
*
* 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.spf4j.base;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Nonnegative;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.Signed;
import javax.annotation.concurrent.ThreadSafe;
import org.spf4j.base.ExecutionContext.Relation;
import org.spf4j.concurrent.ScalableSequence;
import org.spf4j.concurrent.UIDGenerator;
import org.spf4j.ds.SimpleStack;
/**
* @author Zoltan Farkas
*/
@ThreadSafe
@ParametersAreNonnullByDefault
public final class ExecutionContexts {
private static final UIDGenerator ID_GEN = new UIDGenerator(new ScalableSequence(0, 10), "X", 1544368928196L);
public static final long DEFAULT_TIMEOUT_NANOS
= Long.getLong("spf4j.execContext.defaultTimeoutNanos", TimeUnit.HOURS.toNanos(8));
private static final ThreadLocal> EXEC_CTX =
new ThreadLocal>() {
@Override
protected SimpleStack initialValue() {
return new SimpleStack<>(4);
}
};
private static final ThreadLocalContextAttacher DEFAULT_TL_ATTACHER = new ThreadLocalContextAttacherImpl();
private static final ExecutionContextFactory CTX_FACTORY = initFactory();
private static final ThreadLocalContextAttacher TL_ATTACHER = initTLAttacher();
private ExecutionContexts() {
}
private static ThreadLocalContextAttacher initTLAttacher() {
String factoryClass = System.getProperty("spf4j.execContext.tlAttacherClass");
ThreadLocalContextAttacher factory;
if (factoryClass == null) {
factory = DEFAULT_TL_ATTACHER;
} else {
try {
factory = ((Class) Class.forName(factoryClass)).getConstructor().newInstance();
} catch (ClassNotFoundException | InstantiationException | NoSuchMethodException
| InvocationTargetException | IllegalAccessException ex) {
throw new ExceptionInInitializerError(ex);
}
}
return factory;
}
private static ExecutionContextFactory initFactory() {
String factoryClass = System.getProperty("spf4j.execContext.factoryClass");
ExecutionContextFactory factory;
if (factoryClass == null) {
factory = new BasicExecutionContextFactory();
} else {
try {
factory = ((Class>) Class.forName(factoryClass))
.getConstructor().newInstance();
} catch (ClassNotFoundException | InstantiationException | NoSuchMethodException
| InvocationTargetException | IllegalAccessException ex) {
throw new ExceptionInInitializerError(ex);
}
}
String factoryWrapperClass = System.getProperty("spf4j.execContext.factoryWrapperClass");
if (factoryWrapperClass != null) {
try {
factory = (ExecutionContextFactory) Class.forName(factoryWrapperClass)
.getConstructor(ExecutionContextFactory.class).newInstance(factory);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| NoSuchMethodException | InvocationTargetException ex) {
throw new ExceptionInInitializerError(ex);
}
}
return factory;
}
public static ThreadLocalContextAttacher defaultThreadLocalAttacher() {
return DEFAULT_TL_ATTACHER;
}
public static ThreadLocalContextAttacher threadLocalAttacher() {
return TL_ATTACHER;
}
public static CharSequence genId() {
return ID_GEN.next();
}
public static ExecutionContextFactory getContextFactory() {
return CTX_FACTORY;
}
@Nullable
public static ExecutionContext current() {
return EXEC_CTX.get().peek();
}
public static boolean inCurrentThread(final ExecutionContext ctx) {
return EXEC_CTX.get().contains(ctx);
}
public static void clearCurrentThread() {
EXEC_CTX.get().clear();
}
/**
* start a execution context.
*
* @param deadlineNanos the deadline for this context. (System.nanotime)
* @return the execution context.
*/
public static ExecutionContext start(final long startTimeNanos, final long deadlineNanos) {
return start("anon", null, startTimeNanos, deadlineNanos);
}
/**
* start a execution context.
*
* @param timeout
* @param tu
* @return
*/
public static ExecutionContext start(final long timeout, final TimeUnit tu) {
return start("anon", current(), timeout, tu);
}
public static ExecutionContext start(final String opname) {
return start(opname, current(), DEFAULT_TIMEOUT_NANOS, TimeUnit.NANOSECONDS);
}
public static ExecutionContext start(final String opname, final long timeout, final TimeUnit tu) {
return start(opname, current(), timeout, tu);
}
public static ExecutionContext start(@Nullable final ExecutionContext parent, final long timeout, final TimeUnit tu) {
return start("anon", parent, timeout, tu);
}
public static ExecutionContext start(@Nullable final ExecutionContext parent) {
long nanoTime = TimeSource.nanoTime();
return start(parent, nanoTime, parent != null ? parent.getDeadlineNanos() : nanoTime + DEFAULT_TIMEOUT_NANOS);
}
public static ExecutionContext start(@Nullable final ExecutionContext parent,
final long startTimeNanos, final long deadlineNanos) {
return start("anon", parent, startTimeNanos, deadlineNanos);
}
public static ExecutionContext start(final String name, final long startTimeNanos, final long deadlineNanos) {
return start(name, current(), startTimeNanos, deadlineNanos);
}
public static ExecutionContext start(final String name, final long deadlineNanos) {
return start(name, current(), TimeSource.nanoTime(), deadlineNanos);
}
public static ExecutionContext start(final String name,
@Nullable final ExecutionContext parent) {
long nanoTime = TimeSource.nanoTime();
return start(name, parent, nanoTime, parent != null ? parent.getDeadlineNanos()
: nanoTime + DEFAULT_TIMEOUT_NANOS);
}
public static ExecutionContext start(final String name,
@Nullable final ExecutionContext parent, final long timeout, final TimeUnit tu) {
return start(name, null, parent, timeout, tu);
}
public static ExecutionContext start(final String name, @Nullable final CharSequence id,
@Nullable final ExecutionContext parent, final long timeout, final TimeUnit tu) {
long nanoTime = TimeSource.nanoTime();
return start(name, id, parent, nanoTime, computeDeadline(nanoTime, parent, tu, timeout));
}
public static ExecutionContext createDetached(final String name,
@Nullable final ExecutionContext parent, final long timeout, final TimeUnit tu) {
long nanoTime = TimeSource.nanoTime();
return createDetached(name, parent, nanoTime, computeDeadline(nanoTime, parent, tu, timeout));
}
public static ExecutionContext start(final String name,
@Nullable final ExecutionContext parent, final long deadlineNanos) {
return start(name, parent, TimeSource.nanoTime(), deadlineNanos);
}
public static ExecutionContext start(final String name,
@Nullable final ExecutionContext parent, final long startTimeNanos, final long deadlineNanos) {
return start(name, null, parent, startTimeNanos, deadlineNanos);
}
public static ExecutionContext start(final String name, @Nullable final CharSequence id,
@Nullable final ExecutionContext parent, final long startTimeNanos, final long deadlineNanos) {
return start(name, id, parent, Relation.CHILD_OF, startTimeNanos, deadlineNanos);
}
public static ExecutionContext start(final String name, @Nullable final CharSequence id,
@Nullable final ExecutionContext parent, final Relation relation,
final long startTimeNanos, final long deadlineNanos) {
ExecutionContext nCtx = CTX_FACTORY.start(name, id, parent, relation,
startTimeNanos, deadlineNanos);
nCtx.attach();
return nCtx;
}
public static ExecutionContext createDetached(final String name,
@Nullable final ExecutionContext parent, final long startTimeNanos, final long deadlineNanos) {
return createDetached(name, parent, Relation.CHILD_OF, startTimeNanos, deadlineNanos);
}
public static ExecutionContext createDetached(final String name, @Nullable final ExecutionContext parent,
final Relation relation, final long startTimeNanos, final long deadlineNanos) {
return createDetached(name, null, parent, relation, startTimeNanos, deadlineNanos);
}
public static ExecutionContext createDetached(final String name,
@Nullable final CharSequence id, @Nullable final ExecutionContext parent,
final Relation relation, final long startTimeNanos, final long deadlineNanos) {
return CTX_FACTORY.start(name, id, parent, relation, startTimeNanos, deadlineNanos);
}
public static long getContextDeadlineNanos() {
ExecutionContext ec = ExecutionContexts.current();
if (ec == null) {
return TimeSource.nanoTime() + DEFAULT_TIMEOUT_NANOS;
} else {
return ec.getDeadlineNanos();
}
}
public static long getContextDeadlineNanos(final long currentTime) {
ExecutionContext ec = ExecutionContexts.current();
if (ec == null) {
return currentTime + DEFAULT_TIMEOUT_NANOS;
} else {
return ec.getDeadlineNanos();
}
}
@Signed
public static long getTimeRelativeToDeadline(final TimeUnit unit) {
return unit.convert(getContextDeadlineNanos() - TimeSource.nanoTime(), TimeUnit.NANOSECONDS);
}
@Nonnegative
public static long getTimeToDeadline(final TimeUnit unit) throws TimeoutException {
long timeRelativeToDeadline = getTimeRelativeToDeadline(unit);
if (timeRelativeToDeadline <= 0) {
throw new TimeoutException("Deadline exceeded by " + (-timeRelativeToDeadline) + ' ' + unit);
}
return timeRelativeToDeadline;
}
@Nonnegative
public static long getTimeToDeadlineUnchecked(final TimeUnit unit) {
long timeRelativeToDeadline = getTimeRelativeToDeadline(unit);
if (timeRelativeToDeadline <= 0) {
throw new UncheckedTimeoutException("Deadline exceeded by " + (-timeRelativeToDeadline) + ' ' + unit);
}
return timeRelativeToDeadline;
}
@Nonnegative
public static int getTimeToDeadlineInt(final TimeUnit unit) throws TimeoutException {
long timeRelativeToDeadline = getTimeToDeadline(unit);
if (timeRelativeToDeadline > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else {
return (int) timeRelativeToDeadline;
}
}
@Nonnegative
public static long getMillisToDeadline() throws TimeoutException {
return getTimeToDeadline(TimeUnit.MILLISECONDS);
}
@Nonnegative
public static int getSecondsToDeadline() throws TimeoutException {
long secondsToDeadline = getTimeToDeadline(TimeUnit.SECONDS);
if (secondsToDeadline > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else {
return (int) secondsToDeadline;
}
}
public static long computeDeadline(final long timeout, final TimeUnit unit) {
return computeDeadline(current(), timeout, unit);
}
public static long computeTimeout(final long timeout, final TimeUnit unit) throws TimeoutException {
return unit.convert(computeTimeoutDeadline(current(), unit, timeout).getTimeoutNanos(), TimeUnit.NANOSECONDS);
}
/**
* @deprecated use variant where the value and unit are it the natural order.
*/
@Deprecated
public static long computeDeadline(@Nullable final ExecutionContext current,
final TimeUnit unit, final long timeout) {
return computeDeadline(current, timeout, unit);
}
public static long computeDeadline(@Nullable final ExecutionContext current,
final long timeout, final TimeUnit unit) {
if (current == null) {
return TimeSource.getDeadlineNanos(timeout, unit);
}
long nanoTime = TimeSource.nanoTime();
long ctxDeadlinenanos = current.getDeadlineNanos();
long timeoutNanos = unit.toNanos(timeout);
return (ctxDeadlinenanos - nanoTime < timeoutNanos) ? ctxDeadlinenanos : nanoTime + timeoutNanos;
}
public static long computeDeadline(final long startTimeNanos, @Nullable final ExecutionContext current,
final TimeUnit unit, final long timeout) {
if (current == null) {
return TimeSource.getDeadlineNanos(startTimeNanos, timeout, unit);
}
long ctxDeadlinenanos = current.getDeadlineNanos();
long timeoutNanos = unit.toNanos(timeout);
return (ctxDeadlinenanos - startTimeNanos < timeoutNanos) ? ctxDeadlinenanos : startTimeNanos + timeoutNanos;
}
/**
* Compute the actual timeout taking in consideration the context deadline.
* @param current the context
* @param unit timeout unit
* @param timeout timeout value
* @return the earliest timeout (of the provided and context one)
* @throws TimeoutException
*/
public static TimeoutDeadline computeTimeoutDeadline(@Nullable final ExecutionContext current,
final TimeUnit unit, final long timeout) throws TimeoutException {
if (current == null) {
return TimeoutDeadline.of(unit.toNanos(timeout), TimeSource.getDeadlineNanos(timeout, unit));
}
long nanoTime = TimeSource.nanoTime();
long ctxDeadlinenanos = current.getDeadlineNanos();
long timeoutNanos = unit.toNanos(timeout);
long contextTimeoutNanos = ctxDeadlinenanos - nanoTime;
return (contextTimeoutNanos < timeoutNanos)
? TimeoutDeadline.of(contextTimeoutNanos, ctxDeadlinenanos)
: TimeoutDeadline.of(timeoutNanos, nanoTime + timeoutNanos);
}
private static class BasicExecutionContextFactory implements ExecutionContextFactory {
@Override
public ExecutionContext start(final String name, @Nullable final CharSequence id,
@Nullable final ExecutionContext parent, final Relation relation,
final long startTimeNanos, final long deadlineNanos) {
return new BasicExecutionContext(name, id, parent, relation, startTimeNanos, deadlineNanos);
}
}
public static Callable propagatingCallable(final Callable callable) {
ExecutionContext current = current();
return current == null ? callable : propagatingCallable(callable, current);
}
public static Callable propagatingCallable(final Callable callable, final ExecutionContext ctx) {
return new PropagatingCallable(callable, ctx);
}
public static Collection extends Callable> propagatingCallables(
final Collection extends Callable> tasks) {
ExecutionContext current = current();
return current == null ? tasks : propagatingCallables(tasks, current);
}
public static Collection extends Callable> propagatingCallables(
final Collection extends Callable> tasks,
final ExecutionContext ctx) {
return tasks.stream().map(
(c) -> new PropagatingCallable<>(c, ctx))
.collect(Collectors.toCollection(() -> new ArrayList<>(tasks.size())));
}
public static Collection extends Callable> deadlinedPropagatingCallables(
final Collection extends Callable> tasks,
final ExecutionContext ctx, final long deadlineNanos) {
return tasks.stream().map(
(c) -> new PropagatingNamedCallable<>(c, ctx, null, deadlineNanos))
.collect(Collectors.toCollection(() -> new ArrayList<>(tasks.size())));
}
public static Callable deadlinedPropagatingCallable(final Callable callable,
final ExecutionContext ctx, final long deadlineNanos) {
return new PropagatingNamedCallable(callable, ctx, null, deadlineNanos);
}
public static Runnable propagatingRunnable(final Runnable runnable) {
ExecutionContext current = current();
return current == null ? runnable : propagatingRunnable(runnable, current);
}
public static Runnable propagatingRunnable(final Runnable runnable, final ExecutionContext ctx) {
return new PropagatingRunnable(runnable, ctx, null, ctx.getDeadlineNanos());
}
public static Runnable propagatingRunnable(final Runnable runnable, final ExecutionContext ctx,
@Nullable final String name, final long deadlineNanos) {
return new PropagatingRunnable(runnable, ctx, name, deadlineNanos);
}
private static final class PropagatingCallable implements Callable {
private final Callable task;
private final ExecutionContext current;
PropagatingCallable(final Callable task, final ExecutionContext current) {
this.task = task;
this.current = current;
}
@Override
public T call() throws Exception {
try (ExecutionContext ctx = current.startChild(task.toString())) {
return task.call();
}
}
}
public static Callable propagatingCallable(final Callable callable, final ExecutionContext ctx,
@Nullable final String name, final long deadlineNanos) {
return new PropagatingNamedCallable(callable, ctx, name, deadlineNanos);
}
private static final class PropagatingNamedCallable implements Callable, Wrapper> {
private final Callable task;
private final ExecutionContext current;
private final String name;
private final long deadlineNanos;
PropagatingNamedCallable(final Callable task, final ExecutionContext current,
@Nullable final String name, final long deadlineNanos) {
this.task = task;
this.current = current;
this.name = name;
this.deadlineNanos = deadlineNanos;
}
@Override
public T call() throws Exception {
try (ExecutionContext ctx = start(toString(), current, deadlineNanos)) {
return task.call();
}
}
@Override
public String toString() {
return name == null ? task.toString() : name;
}
@Override
public Callable getWrapped() {
return task;
}
}
public static Function propagatingFunction(final Function callable, final ExecutionContext ctx,
@Nullable final String name, final long deadlineNanos) {
return new PropagatingFunction(callable, ctx, name, deadlineNanos);
}
private static final class PropagatingFunction implements Function, Wrapper> {
private final Function function;
private final ExecutionContext current;
private final String name;
private final long deadlineNanos;
PropagatingFunction(final Function task, final ExecutionContext current,
final String name, final long deadlineNanos) {
this.function = task;
this.current = current;
this.name = name;
this.deadlineNanos = deadlineNanos;
}
@Override
public Y apply(final X in) {
try (ExecutionContext ctx = start(toString(), current, deadlineNanos)) {
return function.apply(in);
}
}
@Override
public String toString() {
return name == null ? function.toString() : name;
}
@Override
public Function getWrapped() {
return function;
}
}
public static BiFunction propagatingBiFunction(final BiFunction callable,
final ExecutionContext ctx,
@Nullable final String name, final long deadlineNanos) {
return new PropagatingBiFunction(callable, ctx, name, deadlineNanos);
}
private static final class PropagatingBiFunction
implements BiFunction, Wrapper> {
private final BiFunction function;
private final ExecutionContext current;
private final String name;
private final long deadlineNanos;
PropagatingBiFunction(final BiFunction task, final ExecutionContext current,
final String name, final long deadlineNanos) {
this.function = task;
this.current = current;
this.name = name;
this.deadlineNanos = deadlineNanos;
}
@Override
public Z apply(final X x, final Y y) {
try (ExecutionContext ctx = start(toString(), current, deadlineNanos)) {
return function.apply(x, y);
}
}
@Override
public String toString() {
return name == null ? function.toString() : name;
}
@Override
public BiFunction getWrapped() {
return function;
}
}
public static Consumer propagatingConsumer(final Consumer callable, final ExecutionContext ctx,
@Nullable final String name, final long deadlineNanos) {
return new PropagatingConsumer(callable, ctx, name, deadlineNanos);
}
private static final class PropagatingConsumer implements Consumer, Wrapper> {
private final Consumer function;
private final ExecutionContext current;
private final String name;
private final long deadlineNanos;
PropagatingConsumer(final Consumer task, final ExecutionContext current,
final String name, final long deadlineNanos) {
this.function = task;
this.current = current;
this.name = name;
this.deadlineNanos = deadlineNanos;
}
@Override
public void accept(final X in) {
try (ExecutionContext ctx = start(toString(), current, deadlineNanos)) {
function.accept(in);
}
}
@Override
public String toString() {
return name == null ? function.toString() : name;
}
@Override
public Consumer getWrapped() {
return function;
}
}
public static Supplier propagatingSupplier(final Supplier callable, final ExecutionContext ctx,
@Nullable final String name, final long deadlineNanos) {
return new PropagatingSupplier(callable, ctx, name, deadlineNanos);
}
private static final class PropagatingSupplier implements Supplier, Wrapper> {
private final Supplier function;
private final ExecutionContext current;
private final String name;
private final long deadlineNanos;
PropagatingSupplier(final Supplier task, final ExecutionContext current,
final String name, final long deadlineNanos) {
this.function = task;
this.current = current;
this.name = name;
this.deadlineNanos = deadlineNanos;
}
@Override
public X get() {
try (ExecutionContext ctx = start(toString(), current, deadlineNanos)) {
return function.get();
}
}
@Override
public String toString() {
return name == null ? function.toString() : name;
}
@Override
public Supplier getWrapped() {
return function;
}
}
public static BiConsumer propagatingBiConsumer(final BiConsumer callable,
final ExecutionContext ctx,
@Nullable final String name, final long deadlineNanos) {
return new PropagatingBiConsumer<>(callable, ctx, name, deadlineNanos);
}
private static final class PropagatingBiConsumer implements BiConsumer, Wrapper> {
private final BiConsumer function;
private final ExecutionContext current;
private final String name;
private final long deadlineNanos;
PropagatingBiConsumer(final BiConsumer task, final ExecutionContext current,
final String name, final long deadlineNanos) {
this.function = task;
this.current = current;
this.name = name;
this.deadlineNanos = deadlineNanos;
}
@Override
public void accept(final X x, final Y y) {
try (ExecutionContext ctx = start(toString(), current, deadlineNanos)) {
function.accept(x, y);
}
}
@Override
public String toString() {
return name == null ? function.toString() : name;
}
@Override
public BiConsumer getWrapped() {
return function;
}
}
private static final class PropagatingRunnable implements Runnable, Wrapper {
private final Runnable task;
private final ExecutionContext current;
private final String name;
private final long deadlineNanos;
PropagatingRunnable(final Runnable task, final ExecutionContext current, final String name,
final long deadlineNanos) {
this.task = task;
this.current = current;
this.name = name;
this.deadlineNanos = deadlineNanos;
}
@Override
public void run() {
try (ExecutionContext ctx = start(toString(), current, deadlineNanos)) {
task.run();
}
}
@Override
public Runnable getWrapped() {
return task;
}
@Override
public String toString() {
return name == null ? task.toString() : name;
}
}
private static class ThreadLocalContextAttacherImpl implements ThreadLocalContextAttacher {
@Override
public Attached attach(final ExecutionContext ctx) {
final Thread currentThread = Thread.currentThread();
SimpleStack contextStack = ExecutionContexts.EXEC_CTX.get();
int stackPtr = contextStack.pushAndGetIdx(ctx);
return new AttachedImpl(currentThread, contextStack, ctx, stackPtr);
}
private static class AttachedImpl implements Attached {
private final Thread thread;
private final SimpleStack contextStack;
private final ExecutionContext ctx;
private final int stackPtr;
AttachedImpl(final Thread currentThread,
final SimpleStack contextStack,
final ExecutionContext ctx, final int stackPtr) {
this.thread = currentThread;
this.contextStack = contextStack;
this.ctx = ctx;
this.stackPtr = stackPtr;
}
@Override
public void detach() {
Thread now = Thread.currentThread();
if (now != thread) {
throw new IllegalStateException("Detaching in different thread " + thread + " != " + now);
}
ExecutionContext pop = contextStack.pop();
if (pop != ctx) {
contextStack.push(pop);
throw new IllegalStateException("Detaching ctx that is not attached " + ctx + ", found: " + pop);
}
}
public boolean isTopOfStack() {
return stackPtr == 0;
}
public Thread attachedThread() {
return thread;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy