All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.eventbus.Dispatcher Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * Copyright (C) 2014 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.eventbus;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;

import com.google.common.collect.Queues;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Handler for dispatching events to subscribers, providing different event ordering guarantees that
 * make sense for different situations.
 *
 * 

Note: The dispatcher is orthogonal to the subscriber's {@code Executor}. The dispatcher * controls the order in which events are dispatched, while the executor controls how (i.e. on which * thread) the subscriber is actually called when an event is dispatched to it. * * @author Colin Decker */ @ElementTypesAreNonnullByDefault abstract class Dispatcher { /** * Returns a dispatcher that queues events that are posted reentrantly on a thread that is already * dispatching an event, guaranteeing that all events posted on a single thread are dispatched to * all subscribers in the order they are posted. * *

When all subscribers are dispatched to using a direct executor (which dispatches on * the same thread that posts the event), this yields a breadth-first dispatch order on each * thread. That is, all subscribers to a single event A will be called before any subscribers to * any events B and C that are posted to the event bus by the subscribers to A. */ static Dispatcher perThreadDispatchQueue() { return new PerThreadQueuedDispatcher(); } /** * Returns a dispatcher that queues events that are posted in a single global queue. This behavior * matches the original behavior of AsyncEventBus exactly, but is otherwise not especially useful. * For async dispatch, an {@linkplain #immediate() immediate} dispatcher should generally be * preferable. */ static Dispatcher legacyAsync() { return new LegacyAsyncDispatcher(); } /** * Returns a dispatcher that dispatches events to subscribers immediately as they're posted * without using an intermediate queue to change the dispatch order. This is effectively a * depth-first dispatch order, vs. breadth-first when using a queue. */ static Dispatcher immediate() { return ImmediateDispatcher.INSTANCE; } /** Dispatches the given {@code event} to the given {@code subscribers}. */ abstract void dispatch(Object event, Iterator subscribers); /** Implementation of a {@link #perThreadDispatchQueue()} dispatcher. */ private static final class PerThreadQueuedDispatcher extends Dispatcher { // This dispatcher matches the original dispatch behavior of EventBus. /** Per-thread queue of events to dispatch. */ private final ThreadLocal> queue = new ThreadLocal>() { @Override protected Queue initialValue() { return Queues.newArrayDeque(); } }; /** Per-thread dispatch state, used to avoid reentrant event dispatching. */ private final ThreadLocal dispatching = new ThreadLocal() { @Override protected Boolean initialValue() { return false; } }; @Override void dispatch(Object event, Iterator subscribers) { checkNotNull(event); checkNotNull(subscribers); // requireNonNull accommodates Android's @RecentlyNullable annotation on ThreadLocal.get Queue queueForThread = requireNonNull(queue.get()); queueForThread.offer(new Event(event, subscribers)); if (!dispatching.get()) { dispatching.set(true); try { Event nextEvent; while ((nextEvent = queueForThread.poll()) != null) { while (nextEvent.subscribers.hasNext()) { nextEvent.subscribers.next().dispatchEvent(nextEvent.event); } } } finally { dispatching.remove(); queue.remove(); } } } private static final class Event { private final Object event; private final Iterator subscribers; private Event(Object event, Iterator subscribers) { this.event = event; this.subscribers = subscribers; } } } /** Implementation of a {@link #legacyAsync()} dispatcher. */ private static final class LegacyAsyncDispatcher extends Dispatcher { // This dispatcher matches the original dispatch behavior of AsyncEventBus. // // We can't really make any guarantees about the overall dispatch order for this dispatcher in // a multithreaded environment for a couple of reasons: // // 1. Subscribers to events posted on different threads can be interleaved with each other // freely. (A event on one thread, B event on another could yield any of // [a1, a2, a3, b1, b2], [a1, b2, a2, a3, b2], [a1, b2, b3, a2, a3], etc.) // 2. It's possible for subscribers to actually be dispatched to in a different order than they // were added to the queue. It's easily possible for one thread to take the head of the // queue, immediately followed by another thread taking the next element in the queue. That // second thread can then dispatch to the subscriber it took before the first thread does. // // All this makes me really wonder if there's any value in queueing here at all. A dispatcher // that simply loops through the subscribers and dispatches the event to each would actually // probably provide a stronger order guarantee, though that order would obviously be different // in some cases. /** Global event queue. */ private final ConcurrentLinkedQueue queue = Queues.newConcurrentLinkedQueue(); @Override void dispatch(Object event, Iterator subscribers) { checkNotNull(event); while (subscribers.hasNext()) { queue.add(new EventWithSubscriber(event, subscribers.next())); } EventWithSubscriber e; while ((e = queue.poll()) != null) { e.subscriber.dispatchEvent(e.event); } } private static final class EventWithSubscriber { private final Object event; private final Subscriber subscriber; private EventWithSubscriber(Object event, Subscriber subscriber) { this.event = event; this.subscriber = subscriber; } } } /** Implementation of {@link #immediate()}. */ private static final class ImmediateDispatcher extends Dispatcher { private static final ImmediateDispatcher INSTANCE = new ImmediateDispatcher(); @Override void dispatch(Object event, Iterator subscribers) { checkNotNull(event); while (subscribers.hasNext()) { subscribers.next().dispatchEvent(event); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy