reactor / BlockHound

Java agent to detect blocking calls from non-blocking threads.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Provide example and better document disallowBlockingCallsInside

simonbasle opened this issue · comments

After some trial-and-error, here is an example that demonstrates the feature:

public class BlockingDisallowTest {

    static {
        BlockHound.install(b -> b
                .allowBlockingCallsInside(NonBlockingClass.class.getName(), "outer")
                .disallowBlockingCallsInside(NonBlockingClass.class.getName(), "inner")
        );
    }

    @Test
    public void shouldDisallow() throws InterruptedException {
        NonBlockingClass nbc = new NonBlockingClass();
        AtomicReference<BlockingOperationError> boeRef = new AtomicReference<>();

        //to trip BlockHound in the first place, we must be in a nonblocking thread
        CountDownLatch latch = new CountDownLatch(1);
        Schedulers.parallel().schedule(() -> {
            try {
                nbc.outer();
            }
            catch (BlockingOperationError boe) {
                boeRef.set(boe);
            }
            finally {
                latch.countDown();
            }
        });

        latch.await(5, TimeUnit.SECONDS);

        //given the configuration we expect that yield is allowed, but the sleep inside example2 isn't
        assertThat(boeRef.get())
                .isNotNull()
                .hasMessage("Blocking call! java.lang.Thread.sleep")
                .hasStackTraceContaining("at com.example.BlockingDisallowTest$NonBlockingClass.inner")
                .hasStackTraceContaining("at com.example.BlockingDisallowTest$NonBlockingClass.outer");
    }

    static class NonBlockingClass {

        String inner() {
            try {
                //if this trips BlockHound, the test fails (inner not in the stacktrace)
                Thread.sleep(50);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "example";
        }

        String outer() {
            try {
                Thread.sleep(50);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            Thread.yield();
            return inner();
        }
    }
}

It should be noted that disallow seems to only make sense as an inner exception to an outer allow case:

  • the reverse can simply be achieved by NOT allowing blocking calls inside outer in the first place, and merely allowing inner
  • it doesn't mark the method as a non-blocking execution context. the method MUST still be called from a thread that is already detected as non-blocking by BlockHound (eg. via the threadPredicate) for the allow/disallow evaluation to take place