Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document exception handling limitations in TaskDecorator implementations (specifically for ThreadPoolTaskExecutor#submit) #25231

Closed
archiecobbs opened this issue Jun 10, 2020 · 3 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: documentation A documentation task
Milestone

Comments

@archiecobbs
Copy link

Affects: demonstrated against 5.2.6.RELEASE

ThreadPoolTaskExecutor.setTaskDecorator() allows you to, for example, catch and log any exceptions thrown by tasks submitted to the executor. In my application this functionality is very important, because otherwise bugs that cause exceptions would be completely swallowed and lurk indefinitely, unnoticed.

The bug is that this works if you use ThreadPoolTaskExecutor.execute() to submit your task, but it doesn't work if you use ThreadPoolTaskExecutor.submit() to submit your task.

Basically, when invoking any ThreadPoolTaskExecutor method that returns a Future, there is extra wrapping involved that catches and swallows thrown exceptions before they can reach the configured TaskDecorator. This is a reversed wrapping order from that one would expect.

Here's a program that demonstrates the problem:

import org.springframework.core.task.TaskDecorator;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

public class ThreadPoolTaskExecutorBug {

    public static void main(String[] args) throws Exception {

        // Setup executor
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setTaskDecorator(action -> () -> {
            try {
                System.out.println("invoking " + action + ".run()");
                action.run();
                System.out.println("successful return from " + action + ".run()");
            } catch (Throwable t) {
                System.out.println("caught exception from " + action + ".run(): " + t.getMessage());
            }
        });
        executor.afterPropertiesSet();

        System.out.println();
        System.out.println("TEST #1");
        executor.execute(new Action(1));
        Thread.sleep(500);

        System.out.println();
        System.out.println("TEST #2");
        executor.submit(new Action(2));
        Thread.sleep(500);

        System.out.println();
        executor.shutdown();
    }

    public static class Action implements Runnable {

        private final int id;

        public Action(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            System.out.println(this + ": run() invoked");
            System.out.println(this + ": run() throwing exception");
            throw new RuntimeException("exception thrown by " + this);
        }

        @Override
        public String toString() {
            return "Action#" + this.id;
        }
    }
}

Here's the output:

Jun 10, 2020 10:32:18 AM org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor initialize
INFO: Initializing ExecutorService

TEST #1
invoking Action#1.run()
Action#1: run() invoked
Action#1: run() throwing exception
caught exception from Action#1.run(): exception thrown by Action#1

TEST #2
invoking java.util.concurrent.FutureTask@62f7c498.run()
Action#2: run() invoked
Action#2: run() throwing exception
successful return from java.util.concurrent.FutureTask@62f7c498.run()

Jun 10, 2020 10:32:19 AM org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor shutdown
INFO: Shutting down ExecutorService

Note that under TEST #2 the action throws an exception, just like in TEST #1, but the configured TaskDecorator never gets a chance to catch and log the exception.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 10, 2020
@jhoeller jhoeller self-assigned this Jun 10, 2020
@jhoeller
Copy link
Contributor

This seems to be a common problem with the JDK's ThreadPoolExecutor that we're building on there: see the note in its afterExecute javadoc: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html#afterExecute-java.lang.Runnable-java.lang.Throwable-

ThreadPoolExecutor.before/afterExecute is essentially a variant of our TaskDecorator, we suffer from the same limitations. Since TaskDecorator does not imply exception handling directly, we cannot manually force Future exceptions there. I'm afraid there is not much we can do about it; this unfortunately will have to be handled within affected TaskDecorator implementations :-(

@jhoeller jhoeller added the in: core Issues in core modules (aop, beans, core, context, expression) label Jun 10, 2020
@archiecobbs
Copy link
Author

Thanks for the quick triage. If it can't be fixed then perhaps at least some additional documentation could be added to clarify the semantics of TaskDecorator, in particular, that exceptions may not reach it.

@jhoeller jhoeller added type: documentation A documentation task and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jun 10, 2020
@jhoeller jhoeller changed the title ThreadPoolTaskExecutor.submit() ignores configured TaskDecorator ThreadPoolTaskExecutor.submit() exposes FutureTask to TaskDecorator (no regular exception handling) Jun 10, 2020
@jhoeller jhoeller added this to the 5.2.8 milestone Jun 10, 2020
@jhoeller jhoeller changed the title ThreadPoolTaskExecutor.submit() exposes FutureTask to TaskDecorator (no regular exception handling) Document exception handling limitations in TaskDecorator implementations (specifically for ThreadPoolTaskExecutor#submit) Jun 10, 2020
@jhoeller jhoeller added the for: backport-to-5.1.x Marks an issue as a candidate for backport to 5.1.x label Jun 10, 2020
@spring-projects-issues spring-projects-issues added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-5.1.x Marks an issue as a candidate for backport to 5.1.x labels Jun 10, 2020
@archiecobbs
Copy link
Author

Excellent - thank you.

FelixFly pushed a commit to FelixFly/spring-framework that referenced this issue Aug 16, 2020
zx20110729 pushed a commit to zx20110729/spring-framework that referenced this issue Feb 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

3 participants