001/*
002 * Copyright (C) 2014 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package org.openstreetmap.josm.eventbus;
016
017import java.util.ArrayDeque;
018import java.util.Iterator;
019import java.util.Objects;
020import java.util.Queue;
021import java.util.concurrent.ConcurrentLinkedQueue;
022
023/**
024 * Handler for dispatching events to subscribers, providing different event ordering guarantees that
025 * make sense for different situations.
026 *
027 * <p><b>Note:</b> The dispatcher is orthogonal to the subscriber's {@code Executor}. The dispatcher
028 * controls the order in which events are dispatched, while the executor controls how (i.e. on which
029 * thread) the subscriber is actually called when an event is dispatched to it.
030 *
031 * @author Colin Decker
032 */
033abstract class Dispatcher {
034
035  /**
036   * Returns a dispatcher that queues events that are posted reentrantly on a thread that is already
037   * dispatching an event, guaranteeing that all events posted on a single thread are dispatched to
038   * all subscribers in the order they are posted.
039   *
040   * <p>When all subscribers are dispatched to using a <i>direct</i> executor (which dispatches on
041   * the same thread that posts the event), this yields a breadth-first dispatch order on each
042   * thread. That is, all subscribers to a single event A will be called before any subscribers to
043   * any events B and C that are posted to the event bus by the subscribers to A.
044   * @return dispatcher
045   */
046  static Dispatcher perThreadDispatchQueue() {
047    return new PerThreadQueuedDispatcher();
048  }
049
050  /**
051   * Returns a dispatcher that queues events that are posted in a single global queue. This behavior
052   * matches the original behavior of AsyncEventBus exactly, but is otherwise not especially useful.
053   * For async dispatch, an {@linkplain #immediate() immediate} dispatcher should generally be
054   * preferable.
055   * @return dispatcher
056   */
057  static Dispatcher legacyAsync() {
058    return new LegacyAsyncDispatcher();
059  }
060
061  /**
062   * Returns a dispatcher that dispatches events to subscribers immediately as they're posted
063   * without using an intermediate queue to change the dispatch order. This is effectively a
064   * depth-first dispatch order, vs. breadth-first when using a queue.
065   * @return dispatcher
066   */
067  static Dispatcher immediate() {
068    return ImmediateDispatcher.INSTANCE;
069  }
070
071  /**
072   * Dispatches the given {@code event} to the given {@code subscribers}.
073   * @param event event to dispatch
074   * @param subscribers subscribers to notify
075   */
076  abstract void dispatch(Object event, Iterator<Subscriber> subscribers);
077
078  /** Implementation of a {@link #perThreadDispatchQueue()} dispatcher. */
079  private static final class PerThreadQueuedDispatcher extends Dispatcher {
080
081    // This dispatcher matches the original dispatch behavior of EventBus.
082
083    /** Per-thread queue of events to dispatch. */
084    private final ThreadLocal<Queue<Event>> queue =
085        new ThreadLocal<Queue<Event>>() {
086          @Override
087          protected Queue<Event> initialValue() {
088            return new ArrayDeque<>();
089          }
090        };
091
092    /** Per-thread dispatch state, used to avoid reentrant event dispatching. */
093    private final ThreadLocal<Boolean> dispatching =
094        new ThreadLocal<Boolean>() {
095          @Override
096          protected Boolean initialValue() {
097            return false;
098          }
099        };
100
101    @Override
102    void dispatch(Object event, Iterator<Subscriber> subscribers) {
103      Objects.requireNonNull(event);
104      Objects.requireNonNull(subscribers);
105      Queue<Event> queueForThread = queue.get();
106      queueForThread.offer(new Event(event, subscribers));
107
108      if (!dispatching.get()) {
109        dispatching.set(true);
110        try {
111          Event nextEvent;
112          while ((nextEvent = queueForThread.poll()) != null) {
113            while (nextEvent.subscribers.hasNext()) {
114              nextEvent.subscribers.next().dispatchEvent(nextEvent.event);
115            }
116          }
117        } finally {
118          dispatching.remove();
119          queue.remove();
120        }
121      }
122    }
123
124    private static final class Event {
125      private final Object event;
126      private final Iterator<Subscriber> subscribers;
127
128      private Event(Object event, Iterator<Subscriber> subscribers) {
129        this.event = event;
130        this.subscribers = subscribers;
131      }
132    }
133  }
134
135  /** Implementation of a {@link #legacyAsync()} dispatcher. */
136  private static final class LegacyAsyncDispatcher extends Dispatcher {
137
138    // This dispatcher matches the original dispatch behavior of AsyncEventBus.
139    //
140    // We can't really make any guarantees about the overall dispatch order for this dispatcher in
141    // a multithreaded environment for a couple reasons:
142    //
143    // 1. Subscribers to events posted on different threads can be interleaved with each other
144    //    freely. (A event on one thread, B event on another could yield any of
145    //    [a1, a2, a3, b1, b2], [a1, b2, a2, a3, b2], [a1, b2, b3, a2, a3], etc.)
146    // 2. It's possible for subscribers to actually be dispatched to in a different order than they
147    //    were added to the queue. It's easily possible for one thread to take the head of the
148    //    queue, immediately followed by another thread taking the next element in the queue. That
149    //    second thread can then dispatch to the subscriber it took before the first thread does.
150    //
151    // All this makes me really wonder if there's any value in queueing here at all. A dispatcher
152    // that simply loops through the subscribers and dispatches the event to each would actually
153    // probably provide a stronger order guarantee, though that order would obviously be different
154    // in some cases.
155
156    /** Global event queue. */
157    private final ConcurrentLinkedQueue<EventWithSubscriber> queue =
158        new ConcurrentLinkedQueue<>();
159
160    @Override
161    void dispatch(Object event, Iterator<Subscriber> subscribers) {
162      Objects.requireNonNull(event);
163      while (subscribers.hasNext()) {
164        queue.add(new EventWithSubscriber(event, subscribers.next()));
165      }
166
167      EventWithSubscriber e;
168      while ((e = queue.poll()) != null) {
169        e.subscriber.dispatchEvent(e.event);
170      }
171    }
172
173    private static final class EventWithSubscriber {
174      private final Object event;
175      private final Subscriber subscriber;
176
177      private EventWithSubscriber(Object event, Subscriber subscriber) {
178        this.event = event;
179        this.subscriber = subscriber;
180      }
181    }
182  }
183
184  /** Implementation of {@link #immediate()}. */
185  private static final class ImmediateDispatcher extends Dispatcher {
186    private static final ImmediateDispatcher INSTANCE = new ImmediateDispatcher();
187
188    @Override
189    void dispatch(Object event, Iterator<Subscriber> subscribers) {
190      Objects.requireNonNull(event);
191      while (subscribers.hasNext()) {
192        subscribers.next().dispatchEvent(event);
193      }
194    }
195  }
196}