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}