io.vertx.ext.sync.Sync Maven / Gradle / Ivy
package io.vertx.ext.sync;
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.FiberExecutorScheduler;
import co.paralleluniverse.fibers.FiberScheduler;
import co.paralleluniverse.fibers.Suspendable;
import co.paralleluniverse.strands.channels.Channel;
import io.vertx.core.*;
import io.vertx.ext.sync.impl.AsyncAdaptor;
import io.vertx.ext.sync.impl.HandlerAdaptor;
import io.vertx.ext.sync.impl.HandlerReceiverAdaptorImpl;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
/**
* This class contains various static methods to allowing events and asynchronous results to be accessed
* in a synchronous way.
*
* @author Tim Fox
*/
public class Sync {
private static final String FIBER_SCHEDULER_CONTEXT_KEY = "__vertx-sync.fiberScheduler";
/**
* Invoke an asynchronous operation and obtain the result synchronous.
* The fiber will be blocked until the result is available. No kernel thread is blocked.
*
* @param consumer this should encapsulate the asynchronous operation. The handler is passed to it.
* @param the type of the result
* @return the result
*/
@Suspendable
public static T awaitResult(Consumer>> consumer) {
try {
return new AsyncAdaptor() {
@Override
@Suspendable
protected void requestAsync() {
try {
consumer.accept(this);
} catch (Exception e) {
throw new VertxException(e);
}
}
}.run();
} catch (Throwable t) {
throw new VertxException(t);
}
}
/**
* Invoke an asynchronous operation and obtain the result synchronous.
* The fiber will be blocked until the result is available. No kernel thread is blocked.
*
* @param consumer this should encapsulate the asynchronous operation. The handler is passed to it.
* @param timeout In milliseconds when to cancel the awaited result
* @param the type of the result
* @return the result or null in case of a time out
*/
@Suspendable
public static T awaitResult(Consumer>> consumer, long timeout) {
try {
return new AsyncAdaptor() {
@Override
@Suspendable
protected void requestAsync() {
try {
consumer.accept(this);
} catch (Exception e) {
throw new VertxException(e);
}
}
}.run(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException to) {
return null;
} catch (Throwable t) {
throw new VertxException(t);
}
}
/**
* Receive a single event from a handler synchronously.
* The fiber will be blocked until the event occurs. No kernel thread is blocked.
*
* @param consumer this should encapsulate the setting of the handler to receive the event. The handler is passed to it.
* @param the type of the event
* @return the event
*/
@Suspendable
public static T awaitEvent(Consumer> consumer) {
try {
return new HandlerAdaptor() {
@Override
@Suspendable
protected void requestAsync() {
try {
consumer.accept(this);
} catch (Exception e) {
throw new VertxException(e);
}
}
}.run();
} catch (Throwable t) {
throw new VertxException(t);
}
}
/**
* Receive a single event from a handler synchronously.
* The fiber will be blocked until the event occurs. No kernel thread is blocked.
*
* @param consumer this should encapsulate the setting of the handler to receive the event. The handler is passed to it.
* @param timeout In milliseconds when to cancel the awaited event
* @param the type of the event
* @return the event
*/
@Suspendable
public static T awaitEvent(Consumer> consumer, long timeout) {
try {
return new HandlerAdaptor() {
@Suspendable
@Override
protected void requestAsync() {
try {
consumer.accept(this);
} catch (Exception e) {
throw new VertxException(e);
}
}
}.run(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException to) {
return null;
} catch (Throwable t) {
throw new VertxException(t);
}
}
/**
* Convert a standard handler to a handler which runs on a fiber. This is necessary if you want to do fiber blocking
* synchronous operations in your handler.
*
* @param handler the standard handler
* @param the event type of the handler
* @return a wrapped handler that runs the handler on a fiber
*/
@Suspendable
public static Handler fiberHandler(Handler handler) {
FiberScheduler scheduler = getContextScheduler();
return p -> new Fiber(scheduler, () -> handler.handle(p)).start();
}
/**
* Create an adaptor that converts a stream of events from a handler into a receiver which allows the events to be
* received synchronously.
*
* @param the type of the event
* @return the adaptor
*/
@Suspendable
public static HandlerReceiverAdaptor streamAdaptor() {
return new HandlerReceiverAdaptorImpl<>(getContextScheduler());
}
/**
* Like {@link #streamAdaptor()} but using the specified Quasar `Channel` instance. This is useful if you want to
* fine-tune the behaviour of the adaptor.
*
* @param channel the Quasar channel
* @param the type of the event
* @return the adaptor
*/
@Suspendable
public static HandlerReceiverAdaptor streamAdaptor(Channel channel) {
return new HandlerReceiverAdaptorImpl<>(getContextScheduler(), channel);
}
/**
* Get the `FiberScheduler` for the current context. There should be only one instance per context.
* @return the scheduler
*/
@Suspendable
public static FiberScheduler getContextScheduler() {
Context context = Vertx.currentContext();
if (context == null) {
throw new IllegalStateException("Not in context");
}
if (!context.isEventLoopContext()) {
throw new IllegalStateException("Not on event loop");
}
// We maintain one scheduler per context
FiberScheduler scheduler = context.get(FIBER_SCHEDULER_CONTEXT_KEY);
if (scheduler == null) {
Thread eventLoop = Thread.currentThread();
scheduler = new FiberExecutorScheduler("vertx.contextScheduler", command -> {
if (Thread.currentThread() != eventLoop) {
context.runOnContext(v -> command.run());
} else {
// Just run directly
command.run();
}
});
context.put(FIBER_SCHEDULER_CONTEXT_KEY, scheduler);
}
return scheduler;
}
/**
* Remove the scheduler for the current context
*/
@Suspendable
public static void removeContextScheduler() {
Context context = Vertx.currentContext();
if (context != null) {
context.remove(FIBER_SCHEDULER_CONTEXT_KEY);
}
}
}