Search in sources :

Example 1 with DefaultExecution

use of ratpack.exec.internal.DefaultExecution in project ratpack by ratpack.

the class Blocking method get.

/**
   * Performs a blocking operation on a separate thread, returning a promise for its value.
   * <p>
   * This method should be used to perform blocking IO, or to perform any operation that synchronously waits for something to happen.
   * The given factory function will be executed on a thread from a special pool for such operations (i.e. not a thread from the main compute event loop).
   * <p>
   * The operation should do as little computation as possible.
   * It should just perform the blocking operation and immediately return the result.
   * Performing computation during the operation will degrade performance.
   *
   * @param factory the operation that blocks
   * @param <T> the type of value created by the operation
   * @return a promise for the return value of the given blocking operation
   */
public static <T> Promise<T> get(Factory<T> factory) {
    return new DefaultPromise<>(downstream -> {
        DefaultExecution execution = DefaultExecution.require();
        EventLoop eventLoop = execution.getEventLoop();
        execution.delimit(downstream::error, continuation -> eventLoop.execute(() -> CompletableFuture.supplyAsync(new Supplier<Result<T>>() {

            Result<T> result;

            @Override
            public Result<T> get() {
                try {
                    DefaultExecution.THREAD_BINDING.set(execution);
                    intercept(execution, execution.getAllInterceptors().iterator(), () -> {
                        try {
                            result = Result.success(factory.create());
                        } catch (Throwable e) {
                            result = Result.error(e);
                        }
                    });
                    return result;
                } catch (Throwable e) {
                    DefaultExecution.interceptorError(e);
                    return result;
                } finally {
                    DefaultExecution.THREAD_BINDING.remove();
                }
            }
        }, execution.getController().getBlockingExecutor()).thenAcceptAsync(v -> continuation.resume(() -> downstream.accept(v)), eventLoop)));
    });
}
Also used : EventLoop(io.netty.channel.EventLoop) DefaultExecution(ratpack.exec.internal.DefaultExecution) DefaultPromise(ratpack.exec.internal.DefaultPromise) Supplier(java.util.function.Supplier)

Example 2 with DefaultExecution

use of ratpack.exec.internal.DefaultExecution in project ratpack by ratpack.

the class Blocking method on.

/**
   * Blocks execution waiting for this promise to complete and returns the promised value.
   * <p>
   * This method allows the use of asynchronous API, by synchronous API.
   * This may occur when integrating with other libraries that are not asynchronous.
   * The following example simulates using a library that takes a callback that is expected to produce a value synchronously,
   * but where the production of the value is actually asynchronous.
   *
   * <pre class="java">{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.Blocking;
   * import ratpack.exec.Promise;
   * import ratpack.func.Factory;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   static <T> T produceSync(Factory<? extends T> factory) throws Exception {
   *     return factory.create();
   *   }
   *
   *   public static void main(String... args) throws Exception {
   *     ExecResult<String> result = ExecHarness.yieldSingle(e ->
   *       Blocking.get(() ->
   *         produceSync(() ->
   *           Blocking.on(Promise.value("foo")) // block and wait for the promised value
   *         )
   *       )
   *     );
   *
   *     assertEquals("foo", result.getValue());
   *   }
   * }
   * }</pre>
   *
   * <p>
   * <b>Important:</b> this method can only be used inside a {@link Blocking} function.
   * That is, it can only be used from a Ratpack managed blocking thread.
   * If it is called on a non Ratpack managed blocking thread it will immediately throw an {@link ExecutionException}.
   * <p>
   * When this method is called, the promise will be subscribed to on a compute thread while the blocking thread waits.
   * When the promised value has been produced, and the compute thread segment has completed, the value will be returned
   * allowing execution to continue on the blocking thread.
   * The following example visualises this flow by capturing the sequence of events via an {@link ExecInterceptor}.
   *
   * <pre class="java">{@code
   * import ratpack.test.exec.ExecHarness;
   * import ratpack.exec.Blocking;
   * import ratpack.exec.Promise;
   * import ratpack.exec.ExecResult;
   * import ratpack.exec.ExecInterceptor;
   *
   * import java.util.List;
   * import java.util.ArrayList;
   * import java.util.Arrays;
   *
   * import static org.junit.Assert.assertEquals;
   *
   * public class Example {
   *   public static void main(String... args) throws Exception {
   *     List<String> events = new ArrayList<>();
   *
   *     ExecHarness.yieldSingle(
   *       r -> r.add(ExecInterceptor.class, (execution, execType, continuation) -> {
   *         events.add(execType + "-start");
   *         try {
   *           continuation.execute();
   *         } finally {
   *           events.add(execType + "-stop");
   *         }
   *       }),
   *       e -> Blocking.get(() -> Blocking.on(Promise.value("foo")))
   *     );
   *
   *     List<String> actualEvents = Arrays.asList(
   *       "COMPUTE-start",
   *       "COMPUTE-stop",
   *         "BLOCKING-start",
   *           "COMPUTE-start",
   *           "COMPUTE-stop",
   *         "BLOCKING-stop",
   *       "COMPUTE-start",
   *       "COMPUTE-stop"
   *     );
   *
   *     assertEquals(actualEvents, events);
   *   }
   * }
   * }</pre>
   *
   * @param promise the promise to block on
   * @param <T> the type of value returned by the promise
   * @return the promised value
   * @throws ExecutionException if not called on a Ratpack managed blocking thread
   * @throws Exception any thrown while producing the value
   */
public static <T> T on(Promise<T> promise) throws Exception {
    ThreadBinding.requireBlockingThread("Blocking.on() can only be used while blocking (i.e. use Blocking.get() first)");
    DefaultExecution backing = DefaultExecution.require();
    CountDownLatch latch = new CountDownLatch(1);
    AtomicReference<Result<T>> resultReference = new AtomicReference<>();
    backing.delimit(t -> {
        resultReference.set(Result.error(t));
        latch.countDown();
    }, continuation -> promise.connect(new Downstream<T>() {

        @Override
        public void success(T value) {
            unlatch(Result.success(value));
        }

        @Override
        public void error(Throwable throwable) {
            unlatch(Result.error(throwable));
        }

        @Override
        public void complete() {
            unlatch(Result.success(null));
        }

        private void unlatch(Result<T> result) {
            continuation.resume(() -> {
                resultReference.set(result);
                latch.countDown();
            });
        }
    }));
    backing.eventLoopDrain();
    latch.await();
    return resultReference.get().getValueOrThrow();
}
Also used : DefaultExecution(ratpack.exec.internal.DefaultExecution) AtomicReference(java.util.concurrent.atomic.AtomicReference) CountDownLatch(java.util.concurrent.CountDownLatch)

Aggregations

DefaultExecution (ratpack.exec.internal.DefaultExecution)2 EventLoop (io.netty.channel.EventLoop)1 CountDownLatch (java.util.concurrent.CountDownLatch)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 Supplier (java.util.function.Supplier)1 DefaultPromise (ratpack.exec.internal.DefaultPromise)1