예제 #1
0
  /**
   * 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;
  }
}
예제 #3
0
/**
 * 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;
  }
}