/** * Create a new single producer RingBuffer with the specified wait strategy. * * @param factory used to create the events within the ring buffer. * @param bufferSize number of elements to create within the ring buffer. * @param waitStrategy used to determine how to wait for new elements to become available. * @param spinObserver called each time the next claim is spinning and waiting for a slot * @see SingleProducerSequencer */ public static <E> RingBuffer<E> createSingleProducer( Supplier<E> factory, int bufferSize, WaitStrategy waitStrategy, Consumer<Void> spinObserver) { SingleProducerSequencer sequencer = new SingleProducerSequencer(bufferSize, waitStrategy, spinObserver); if (PlatformDependent.hasUnsafe() && Util.isPowerOfTwo(bufferSize)) { return new UnsafeRingBuffer<>(factory, sequencer); } else { return new NotFunRingBuffer<>(factory, sequencer); } }
/** * @author Stephane Maldini * @since 2.1 */ public class InterruptableSubscriber<T> extends ConsumerSubscriber<T> implements Control { @SuppressWarnings("unused") volatile Subscription subscription; static final AtomicReferenceFieldUpdater<InterruptableSubscriber, Subscription> SUBSCRIPTION = PlatformDependent.newAtomicReferenceFieldUpdater( InterruptableSubscriber.class, "subscription"); static final Subscription CANCELLED = new Subscription() { @Override public void request(long n) {} @Override public void cancel() {} }; static final Subscription UNSUBSCRIBED = new Subscription() { @Override public void request(long n) {} @Override public void cancel() {} }; public InterruptableSubscriber( Consumer<? super T> consumer, Consumer<? super Throwable> errorConsumer, Consumer<Void> completeConsumer) { super(consumer, errorConsumer, completeConsumer); SUBSCRIPTION.lazySet(this, UNSUBSCRIBED); } @Override public void cancel() { if (SUBSCRIPTION.getAndSet(this, CANCELLED) != CANCELLED) { super.cancel(); } } @Override protected final void doNext(T x) { if (subscription == CANCELLED) { throw CancelException.get(); } super.doNext(x); doPostNext(x); } @Override protected final void doSubscribe(Subscription s) { if (SUBSCRIPTION.getAndSet(this, s) != CANCELLED) { doSafeSubscribe(s); } } @Override protected final void doError(Throwable t) { if (SUBSCRIPTION.getAndSet(this, CANCELLED) != CANCELLED) { doSafeError(t); } } @Override protected final void doComplete() { if (SUBSCRIPTION.getAndSet(this, CANCELLED) != CANCELLED) { doSafeComplete(); } } @Override protected void requestMore(long n) { Subscription subscription = SUBSCRIPTION.get(this); if (subscription != UNSUBSCRIBED) { subscription.request(n); } } protected void doSafeSubscribe(Subscription s) { super.doSubscribe(s); } protected void doPostNext(T x) {} protected void doSafeComplete() { super.doComplete(); } protected void doSafeError(Throwable t) { super.doError(t); } @Override public boolean isTerminated() { return SUBSCRIPTION.get(this) == CANCELLED; } @Override public ReactiveStateUtils.Graph debug() { return ReactiveStateUtils.scan(this); } protected boolean isUnsubscribed() { return SUBSCRIPTION.get(this) == UNSUBSCRIBED; } }
/** * A Global Timer * * @author Stephane Maldini * @since 2.5 */ public class GlobalTimer extends HashWheelTimer implements ReactiveState.Trace { private static final class GlobalContext { volatile GlobalTimer timer; } private static final AtomicReferenceFieldUpdater<GlobalContext, GlobalTimer> GLOBAL_TIMER = PlatformDependent.newAtomicReferenceFieldUpdater(GlobalContext.class, "timer"); private static final GlobalContext context = new GlobalContext(); public GlobalTimer() { super("global-timer", 50, DEFAULT_WHEEL_SIZE, new WaitStrategy.Sleeping(), null); } private void _cancel() { super.cancel(); // TimeUtils.disable(); } @Override public void cancel() { // IGNORE } @Override public void start() { // TimeUtils.enable(); super.start(); } /** * Obtain the default global timer from the current context. The globalTimer is created lazily so * it is preferrable to fetch them out of the critical path. * * <p>The global timer will also ignore {@link Timer#cancel()} calls and should be cleaned using * {@link #unregister()} ()}. * * <p>The default globalTimer is a {@link HashWheelTimer}. It is suitable for non blocking * periodic work such as eventing, memory access, lock-free code, dispatching... * * @return the globalTimer, usually a {@link HashWheelTimer} */ public static Timer get() { GlobalTimer t = context.timer; while (null == t) { t = new GlobalTimer(); if (!GLOBAL_TIMER.compareAndSet(context, null, t)) { t = context.timer; t._cancel(); } else { t.start(); break; } } return t; } /** * Clean current global timer references and cancel the respective {@link Timer}. A new global * timer can be assigned later with {@link #get()}. */ public static void unregister() { GlobalTimer timer; while ((timer = GLOBAL_TIMER.getAndSet(context, null)) != null) { timer._cancel(); } } /** * Read if the context timer has been set * * @return true if context timer is initialized */ public static boolean available() { return context.timer != null; } /** * The returned timer SHOULD always be cancelled after use, however global timer will ignore it. * * @return eventually the global timer or if not set a fresh timer. */ public static Timer globalOrNew() { Timer timer = context.timer; if (timer == null) { timer = new HashWheelTimer(50, 64, new WaitStrategy.Sleeping()); timer.start(); } return timer; } }