/interpreter/tags/at2dist170907/src/edu/vub/at/actors/eventloops/EventLoop.java
Java | 255 lines | 85 code | 32 blank | 138 comment | 2 complexity | 1f20deb9709e7aea73fbc87d4700889d MD5 | raw file
1/** 2 * AmbientTalk/2 Project 3 * EventLoop.java created on 27-dec-2006 at 15:14:24 4 * (c) Programming Technology Lab, 2006 - 2007 5 * Authors: Tom Van Cutsem & Stijn Mostinckx 6 * 7 * Permission is hereby granted, free of charge, to any person 8 * obtaining a copy of this software and associated documentation 9 * files (the "Software"), to deal in the Software without 10 * restriction, including without limitation the rights to use, 11 * copy, modify, merge, publish, distribute, sublicense, and/or 12 * sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following 14 * conditions: 15 * 16 * The above copyright notice and this permission notice shall be 17 * included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 * OTHER DEALINGS IN THE SOFTWARE. 27 */ 28package edu.vub.at.actors.eventloops; 29 30import edu.vub.at.util.logging.Logging; 31 32 33 34/** 35 * The EventLoop is the basic concurrency primitive for the AmbientTalk/2 implementation. 36 * An Event Loop consists of an event queue, an event processing thread and an event handler. 37 * The event loop's thread perpetually takes the next event from the event queue and makes 38 * the event loop process it. Hence, the event loop is the event handler in this architecture. 39 * 40 * Event Loops form the reusable core of both actors, remote references and even the virtual machine 41 * itself (i.e. it is the core of both programmer-defined and native actors). 42 * 43 * This is an abstract class. To be usable, subclasses have to provide a meaningful 44 * implemementation strategy for handling events by overriding the handle method. 45 * 46 * @author tvcutsem 47 * @author smostinc 48 */ 49public abstract class EventLoop { 50 51 /** 52 * The event loop's event queue is a synchronized queue of Event objects. 53 * It is the sole communication channel between different event loops. 54 * As such, it is the means by which different actors - and event actors and 55 * their virtual machines - communicate. 56 */ 57 protected final EventQueue eventQueue_; 58 59 /** 60 * Each event loop has an event processor, which is a thread responsible 61 * for perpetually dequeuing events from the event queue and passing them 62 * on to this event loop's handle method. 63 */ 64 protected Thread processor_; 65 66 protected volatile boolean askedToStop_; 67 68 private final String name_; 69 70 /** 71 * Constructs a new event loop with the default processing behaviour. 72 * @param name used for debugging purposes 73 */ 74 public EventLoop(String name) { 75 eventQueue_ = new EventQueue(); 76 askedToStop_ = false; 77 name_ = name; 78 79 processor_ = new EventProcessor(); 80 processor_.start(); 81 } 82 83 public String toString() { 84 return name_; 85 } 86 87 /** 88 * Attempts to cast a {@link Thread} to an {@link EventLoop}. This code performs error checking and should 89 * therefore be used whenever a Thread (typically the current thread) needs to be cast into 90 * an EventLoop 91 * @param t a Java Thread to be cast into an event loop 92 * @return t as an EventLoop 93 * @throws IllegalStateException when the cast failed. 94 */ 95 public static EventLoop toEventLoop(Thread t) throws IllegalStateException { 96 try { 97 EventProcessor processor = (EventProcessor) t; 98 return processor.serving(); 99 } catch (ClassCastException e) { 100 e.printStackTrace(); 101 throw new IllegalStateException("Asked to transform a non-event loop thread to an event loop"); 102 } 103 } 104 105 /** 106 * Allows access to the currently running event loop. 107 * @return the currently serving event loop 108 * @throws IllegalStateException if the current thread is not the thread of an event loop 109 */ 110 public static EventLoop currentEventLoop() throws IllegalStateException { 111 Thread current = Thread.currentThread(); 112 try { 113 EventProcessor processor = (EventProcessor) current; 114 return processor.serving(); 115 } catch (ClassCastException e) { 116 throw new IllegalStateException("Asked for current event loop when none was active"); 117 } 118 } 119 120 /** 121 * Method to interrupt the EventLoop before it starts processing its next event. Note that 122 * this method consequently does not help when en event loop is stuck in an endless loop 123 * while evaluating a single event. 124 */ 125 public final void stopProcessing() { 126 askedToStop_ = true; 127 // explicitly interrupt my event processor because it 128 // may be blocked waiting on other events 129 processor_.interrupt(); 130 } 131 132 /** 133 * When an event loop receives an asynchronously emitted event, this message is 134 * immediately placed into its incoming event queue and will be processed later. 135 * 136 * This method is declared protected such that subclasses can provide a cleaner 137 * interface as to what kind of events can be received by this event loop. 138 * The convention is that a subclass provides a number of methods prefixed with 139 * event_ which call this protected method to schedule a certain event. 140 */ 141 protected final void receive(Event event) { 142 eventQueue_.enqueue(event); 143 } 144 145 /** 146 * Schedules an event in this event loop's queue which will execute the provided 147 * callable object at a later point in time. Moreover, this method immediately 148 * makes the calling thread WAIT for the return value or resulting exception 149 * of the callable. 150 * 151 * Caller must ensure that the thread invoking this method is not this event 152 * loop its own thread, which inevitably leads to deadlock. 153 * 154 * This method is declared protected such that subclasses can provide a cleaner 155 * interface as to what kind of tasks may be scheduled in this event loop. 156 * The convention is that a subclass provides a number of methods prefixed with 157 * sync_event_ which call this protected method to schedule a certain task. 158 * 159 * @param description a description of the task being scheduled, for debugging purposes 160 * @param callable the functor object encapsulating the task to be performed inside the event loop 161 */ 162 protected final Object receiveAndWait(String description, final Callable callable) throws Exception { 163 if (Thread.currentThread() == processor_) { 164 throw new RuntimeException("Potential deadlock detected: " 165 + processor_ + " tried to perform a synchronous operation on itself"); 166 } 167 168 BlockingFuture future = new BlockingFuture(); 169 eventQueue_.enqueue(new FutureEvent(description, future) { 170 private static final long serialVersionUID = 1672724382106164388L; 171 172 public Object execute(Object owner) throws Exception { 173 return callable.call(owner); 174 } 175 }); 176 return future.get(); 177 } 178 179 /** 180 * When an event loop receives an asynchronously emitted event, this message is 181 * immediately placed into its incoming event queue and will be processed later. 182 * Using this method, events are scheduled first in the queue of upcoming events. 183 * 184 * This method is declared protected such that subclasses can provide a cleaner 185 * interface as to what kind of events can be received by this event loop. 186 * The convention is that this method is only used to put back events that could 187 * not be processed due to problems outside of the influence of the interpreter 188 * (e.g. host unreachability). If a subclass provides direct access to this 189 * primitive it should do so in methods prefixed with prioritized_event_ which 190 * call this protected method to schedule a certain event. 191 */ 192 protected final void receivePrioritized(Event event) { 193 eventQueue_.enqueueFirst(event); 194 } 195 196 /** 197 * Subclasses are responsible for defining a meaningful implementation 198 * strategy to handle events from the event queue. 199 * 200 * @param event the event object which was dequeued and which should be processed 201 */ 202 public abstract void handle(Event event); 203 204 protected final EventLoop owner() { return this; } 205 206 /** 207 * Invoked by the event processor thread continually while 208 * the event loop is alive. 209 * 210 * The default behaviour is to continually dequeue events 211 * and make a subclass handle the event. 212 * 213 * Overridable by subclasses to customize event processing. 214 */ 215 protected void execute() { 216 try { 217 Event event = eventQueue_.dequeue(); 218 219 Logging.EventLoop_LOG.info(owner() + " is processing " + event); 220 221 handle(event); 222 } catch (InterruptedException e) { 223 // If interrupted, we may be asked to stop 224 } 225 } 226 227 /** 228 * EventProcessor is a thread subclass whose primary goal is to keep a reference to the 229 * associated event loop, which is used to transform threads into the more manageable 230 * event loops 231 */ 232 public final class EventProcessor extends Thread { 233 234 protected EventProcessor() { 235 setName(toString()); 236 } 237 238 protected EventLoop serving() { return owner(); } 239 240 public final void run() { 241 while(!askedToStop_) { 242 execute(); 243 244 // give other event loops a chance to process an event 245 Thread.yield(); 246 } 247 } 248 249 public String toString() { 250 return "Event Loop " + serving().toString(); 251 } 252 253 } 254 255}