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

org.eclipse.jetty.util.thread.Invocable Maven / Gradle / Ivy

The newest version!
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.util.thread;

import java.util.concurrent.Executor;

/**
 * 

A task (typically either a {@link Runnable} or {@link Callable} * that declares how it will behave when invoked:

*
    *
  • blocking, the invocation will certainly block (e.g. performs blocking I/O)
  • *
  • non-blocking, the invocation will certainly not block
  • *
  • either, the invocation may block
  • *
* *

* Static methods and are provided that allow the current thread to be tagged * with a {@link ThreadLocal} to indicate if it has a blocking invocation type. *

*/ public interface Invocable { Runnable NOOP = () -> {}; ThreadLocal __nonBlocking = new ThreadLocal<>(); /** *

The behavior of an {@link Invocable} when it is invoked.

*

Typically, {@link Runnable}s or {@link org.eclipse.jetty.util.Callback}s declare their * invocation type; this information is then used by the code that should * invoke the {@code Runnable} or {@code Callback} to decide whether to * invoke it directly, or submit it to a thread pool to be invoked by * a different thread.

*/ enum InvocationType { /** *

Invoking the {@link Invocable} may block the invoker thread, * and the invocation may be performed immediately (possibly blocking * the invoker thread) or deferred to a later time, for example * by submitting the {@code Invocable} to a thread pool.

*

This invocation type is suitable for {@code Invocable}s that * call application code, for example to process an HTTP request.

*/ BLOCKING { public void runWithoutBlocking(Runnable task, Executor executor) { executor.execute(task); } }, /** *

Invoking the {@link Invocable} does not block the invoker thread, * and the invocation may be performed immediately in the invoker thread.

*

This invocation type is suitable for {@code Invocable}s that * call implementation code that is guaranteed to never block the * invoker thread.

*/ NON_BLOCKING { public void runWithoutBlocking(Runnable task, Executor ignored) { task.run(); } }, /** *

Invoking the {@link Invocable} may block the invoker thread, * but the invocation cannot be deferred to a later time, differently * from {@link #BLOCKING}.

*

This invocation type is suitable for {@code Invocable}s that * themselves perform the non-deferrable action in a non-blocking way, * thus advancing a possibly stalled system.

*/ EITHER { public void runWithoutBlocking(Runnable task, Executor ignored) { Invocable.invokeNonBlocking(task); } }; /** * Run or Execute the task according to the InvocationType without blocking the caller: *
*
{@link InvocationType#NON_BLOCKING}
*
The task is run directly
*
{@link InvocationType#BLOCKING}
*
The task is executed by the passed executor
*
{@link InvocationType#EITHER}
*
The task is invoked via {@link Invocable#invokeNonBlocking(Runnable)}
*
* @param task The task to run * @param executor The executor to use if necessary */ public abstract void runWithoutBlocking(Runnable task, Executor executor); } /** *

A task with an {@link InvocationType}.

*/ interface Task extends Invocable, Runnable { /** * An abstract partial implementation of Task */ abstract class Abstract implements Task { private final InvocationType type; public Abstract(InvocationType type) { this.type = type; } @Override public InvocationType getInvocationType() { return type; } @Override public String toString() { return String.format("%s@%x[%s]", getClass().getSimpleName(), hashCode(), getInvocationType()); } } } // TODO review. Handy for lambdas that throw (eg LifeCycle#start()) // TODO: there is already java.util.Callable, can we use it? interface Callable extends Invocable { void call() throws Exception; } /** *

A {@link Runnable} decorated with an {@link InvocationType}.

*/ class ReadyTask extends Task.Abstract { private final Runnable task; public ReadyTask(InvocationType type, Runnable task) { super(type); this.task = task; } public Runnable getTask() { return task; } @Override public void run() { task.run(); } @Override public String toString() { return String.format("%s@%x[%s|%s]", getClass().getSimpleName(), hashCode(), getInvocationType(), task); } } /** *

Creates a {@link Task} from the given InvocationType and Runnable.

* * @param type the InvocationType * @param task the Runnable * @return a new Task */ static Task from(InvocationType type, Runnable task) { if (task instanceof Task t && t.getInvocationType() == type) return t; return new ReadyTask(type, task); } /** * Test if the current thread has been tagged as non blocking * * @return True if the task the current thread is running has * indicated that it will not block. */ static boolean isNonBlockingInvocation() { return Boolean.TRUE.equals(__nonBlocking.get()); } /** * Invoke a task with the calling thread, tagged to indicate * that it will not block. * * @param task The task to invoke. */ static void invokeNonBlocking(Runnable task) { Boolean wasNonBlocking = __nonBlocking.get(); try { __nonBlocking.set(Boolean.TRUE); task.run(); } finally { __nonBlocking.set(wasNonBlocking); } } /** * Combine two invocation type. * @param it1 A type * @param it2 Another type * @return The combination of both type, where any tendency to block overrules any non blocking. */ static InvocationType combine(InvocationType it1, InvocationType it2) { if (it1 != null && it2 != null) { if (it1 == it2) return it1; if (it1 == InvocationType.EITHER) return it2; if (it2 == InvocationType.EITHER) return it1; } return InvocationType.BLOCKING; } static InvocationType combineTypes(InvocationType... it) { if (it == null || it.length == 0) return InvocationType.BLOCKING; InvocationType type = it[0]; for (int i = 1; i < it.length; i++) type = combine(type, it[i]); return type; } /** * Get the invocation type of an Object. * * @param o The object to check the invocation type of. * @return If the object is an Invocable, it is coerced and the {@link #getInvocationType()} * used, otherwise {@link InvocationType#BLOCKING} is returned. */ static InvocationType getInvocationType(Object o) { if (o instanceof Invocable) return ((Invocable)o).getInvocationType(); return InvocationType.BLOCKING; } /** * @return The InvocationType of this object */ default InvocationType getInvocationType() { return InvocationType.BLOCKING; } /** * Combine {@link Runnable}s into a single {@link Runnable} that sequentially calls the others. * @param runnables the {@link Runnable}s to combine * @return the combined {@link Runnable} with a combined {@link InvocationType}. */ static Runnable combine(Runnable... runnables) { Runnable result = null; for (Runnable runnable : runnables) { if (runnable == null) continue; if (result == null) { result = runnable; } else { Runnable first = result; result = new Task() { @Override public void run() { first.run(); runnable.run(); } @Override public InvocationType getInvocationType() { return combine(Invocable.getInvocationType(first), Invocable.getInvocationType(runnable)); } }; } } return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy