Abhijith Gururaj
Abhijith Gururaj

Reputation: 465

How to stop/halt the main program/thread from a java agent

I have a gradle test task which runs a list of tests from a given file. Sometimes, any particular test execution simply gets stuck and does not move on to execute the next test(s) in the list.

For this, I am trying to add a java agent which will detect timeouts in each test execution and calls System.exit() in this case. (I know calling System.exit() seems to be a rash decision, but throwing exception does not seem to stop the test execution) The java agent uses byte-buddy advices for doing this.

public class TimerAdvice {
    public static CountDownLatch latch;

    @Advice.OnMethodEnter
    static long enter(@Advice.This Object thisObject,
                      @Advice.Origin String origin,
                      @Advice.Origin("#t #m") String detaildOrigin) throws InterruptedException {
        System.out.println("Timer Advice Enter thread: " + Thread.currentThread().getName() + " time: " + Instant.now().toString());

        latch = new CountDownLatch(1);

        ThreadFactory factory = new MyThreadFactory(new MyExceptionHandler());
        ExecutorService threadPool = Executors.newFixedThreadPool(1, factory);
        threadPool.execute(new TestCallable());

        return System.nanoTime();
    }

    @Advice.OnMethodExit (onThrowable = Throwable.class)
    static void onExit(@Advice.Origin Method method) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        System.out.println("Timer Advice Exit thread: " + Thread.currentThread().getName() + " time: " + Instant.now().toString());
        System.out.println("Counting down");
        latch.countDown();
    }
}

Basically this will spawn a background thread that will wait until the latch is counted down.

public class TestCallable implements Runnable {
    @Override
    public void run()  {
        try {
            latch.await(10, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new IllegalStateException(e.getMessage());
        }
        if(latch.getCount() > 0) {
            System.err.println("Callable thread"
                    + Thread.currentThread().getName() +
                    "TIMEOUT OCCURED!!!!");
            System.exit(1);
        }
    }
}

The latch countDown() method will be called by the method that handles the OnExit advice. Until then, the thread will wait for the specified timeout.

My question is, Why is the System.exit() call not affecting the test execution/jvm When this thread calls the System.exit(), the test thread still continues to execute as if nothing had happened. I would like to stop the execution of the test at this point.

Any suggestions on how should I stop the entire test execution process when the timeout is detected?

Upvotes: 3

Views: 404

Answers (1)

kriegaex
kriegaex

Reputation: 67407

The OP said that my comment concerning security manager helped him find the root cause, so I am converting it into an answer:

As is documented, System.exit() will not shut down the JVM if there is a security manager stopping it from doing so. In that case you should see a SecurityException, though.

A discussion in Gradle issue #11195 mentions a problem that Kafka sporadically exits unexpectedly and suggests a security manager policy for Spring-Kafka stopping it from doing so. This was committed to Spring-Kafka, but - if I understand correctly - not to Gradle.


Another edge case is shutdown hooks: The thread invoking System.exit() blocks until the JVM terminates. If a shutdown hook submits a task to this thread, it leads to a deadlock.

Upvotes: 1

Related Questions