/interpreter/tags/at2dist110511/src/edu/vub/at/actors/eventloops/EventLoop.java
Java | 297 lines | 103 code | 37 blank | 157 comment | 2 complexity | 11091d50b210663ae1fed96a2e42a136 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 java.util.Properties; 31 32import edu.vub.at.util.logging.Logging; 33 34 35 36/** 37 * The EventLoop is the basic concurrency primitive for the AmbientTalk/2 implementation. 38 * An Event Loop consists of an event queue, an event processing thread and an event handler. 39 * The event loop's thread perpetually takes the next event from the event queue and makes 40 * the event loop process it. Hence, the event loop is the event handler in this architecture. 41 * 42 * Event Loops form the reusable core of both actors, remote references and even the virtual machine 43 * itself (i.e. it is the core of both programmer-defined and native actors). 44 * 45 * This is an abstract class. To be usable, subclasses have to provide a meaningful 46 * implemementation strategy for handling events by overriding the handle method. 47 * 48 * @author tvcutsem 49 * @author smostinc 50 */ 51public abstract class EventLoop { 52 53 /** 54 * The event loop's event queue is a synchronized queue of Event objects. 55 * It is the sole communication channel between different event loops. 56 * As such, it is the means by which different actors - and event actors and 57 * their virtual machines - communicate. 58 */ 59 protected final EventQueue eventQueue_; 60 61 /** 62 * Each event loop has an event processor, which is a thread responsible 63 * for perpetually dequeuing events from the event queue and passing them 64 * on to this event loop's handle method. 65 */ 66 protected Thread processor_; 67 68 protected volatile boolean askedToStop_; 69 70 private final String name_; 71 72 private static final String _ENV_AT_STACK_SIZE_ = "AT_STACK_SIZE"; 73 74 /** 75 * Constructs a new event loop with the default processing behaviour. 76 * Note: the creator must explicitly call {@link this#start()} to start 77 * the event loop! 78 * 79 * @param name used for debugging purposes 80 */ 81 public EventLoop(String name) { 82 eventQueue_ = new EventQueue(); 83 askedToStop_ = false; 84 name_ = name; 85 processor_ = new EventProcessor(); 86 } 87 88 public EventLoop(String name, int stackSize) { 89 eventQueue_ = new EventQueue(); 90 askedToStop_ = false; 91 name_ = name; 92 processor_ = new EventProcessor(stackSize); 93 } 94 95 /** 96 * Starts the execution of this event loop. 97 */ 98 public void start() { 99 processor_.start(); 100 } 101 102 public String toString() { 103 return name_; 104 } 105 106 /** 107 * Attempts to cast a {@link Thread} to an {@link EventLoop}. This code performs error checking and should 108 * therefore be used whenever a Thread (typically the current thread) needs to be cast into 109 * an EventLoop 110 * @param t a Java Thread to be cast into an event loop 111 * @return t as an EventLoop 112 * @throws IllegalStateException when the cast failed. 113 */ 114 public static EventLoop toEventLoop(Thread t) throws IllegalStateException { 115 try { 116 EventProcessor processor = (EventProcessor) t; 117 return processor.serving(); 118 } catch (ClassCastException e) { 119 e.printStackTrace(); 120 throw new IllegalStateException("Asked to transform a non-event loop thread to an event loop"); 121 } 122 } 123 124 /** 125 * Allows access to the currently running event loop. 126 * @return the currently serving event loop 127 * @throws IllegalStateException if the current thread is not the thread of an event loop 128 */ 129 public static EventLoop currentEventLoop() throws IllegalStateException { 130 Thread current = Thread.currentThread(); 131 try { 132 EventProcessor processor = (EventProcessor) current; 133 return processor.serving(); 134 } catch (ClassCastException e) { 135 throw new IllegalStateException("Asked for current event loop when none was active"); 136 } 137 } 138 139 /** 140 * Method to interrupt the EventLoop before it starts processing its next event. Note that 141 * this method consequently does not help when en event loop is stuck in an endless loop 142 * while evaluating a single event. 143 */ 144 public final void stopProcessing() { 145 askedToStop_ = true; 146 // explicitly interrupt my event processor because it 147 // may be blocked waiting on other events 148 processor_.interrupt(); 149 } 150 151 /** 152 * When an event loop receives an asynchronously emitted event, this message is 153 * immediately placed into its incoming event queue and will be processed later. 154 * 155 * This method is declared protected such that subclasses can provide a cleaner 156 * interface as to what kind of events can be received by this event loop. 157 * The convention is that a subclass provides a number of methods prefixed with 158 * event_ which call this protected method to schedule a certain event. 159 */ 160 protected final void receive(Event event) { 161 eventQueue_.enqueue(event); 162 } 163 164 /** 165 * Schedules an event in this event loop's queue which will execute the provided 166 * callable object at a later point in time. Moreover, this method immediately 167 * makes the calling thread WAIT for the return value or resulting exception 168 * of the callable. 169 * 170 * Caller must ensure that the thread invoking this method is not this event 171 * loop its own thread, which inevitably leads to deadlock. 172 * 173 * This method is declared protected such that subclasses can provide a cleaner 174 * interface as to what kind of tasks may be scheduled in this event loop. 175 * The convention is that a subclass provides a number of methods prefixed with 176 * sync_event_ which call this protected method to schedule a certain task. 177 * 178 * @param description a description of the task being scheduled, for debugging purposes 179 * @param callable the functor object encapsulating the task to be performed inside the event loop 180 */ 181 protected final Object receiveAndWait(String description, final Callable callable) throws Exception { 182 /*if (Thread.currentThread() == processor_) { 183 throw new RuntimeException("Potential deadlock detected: " 184 + processor_ + " tried to perform a synchronous operation on itself"); 185 } 186 187 BlockingFuture future = new BlockingFuture(); 188 eventQueue_.enqueue(new FutureEvent(description, future) { 189 private static final long serialVersionUID = 1672724382106164388L; 190 191 public Object execute(Object owner) throws Exception { 192 return callable.call(owner); 193 } 194 });*/ 195 BlockingFuture future = receiveAndReturnFuture(description, callable); 196 return future.get(); 197 } 198 199 protected final BlockingFuture receiveAndReturnFuture(String description, final Callable callable) throws Exception { 200 if (Thread.currentThread() == processor_) { 201 throw new RuntimeException("Potential deadlock detected: " 202 + processor_ + " tried to perform a synchronous operation on itself"); 203 } 204 205 BlockingFuture future = new BlockingFuture(); 206 eventQueue_.enqueue(new FutureEvent(description, future) { 207 private static final long serialVersionUID = 1672724382106164388L; 208 209 public Object execute(Object owner) throws Exception { 210 return callable.call(owner); 211 } 212 }); 213 return future; 214 } 215 216 /** 217 * When an event loop receives an asynchronously emitted event, this message is 218 * immediately placed into its incoming event queue and will be processed later. 219 * Using this method, events are scheduled first in the queue of upcoming events. 220 * 221 * This method is declared protected such that subclasses can provide a cleaner 222 * interface as to what kind of events can be received by this event loop. 223 * The convention is that this method is only used to put back events that could 224 * not be processed due to problems outside of the influence of the interpreter 225 * (e.g. host unreachability). If a subclass provides direct access to this 226 * primitive it should do so in methods prefixed with prioritized_event_ which 227 * call this protected method to schedule a certain event. 228 */ 229 protected final void receivePrioritized(Event event) { 230 eventQueue_.enqueueFirst(event); 231 } 232 233 /** 234 * Subclasses are responsible for defining a meaningful implementation 235 * strategy to handle events from the event queue. 236 * 237 * @param event the event object which was dequeued and which should be processed 238 */ 239 public abstract void handle(Event event); 240 241 protected final EventLoop owner() { return this; } 242 243 /** 244 * Invoked by the event processor thread continually while 245 * the event loop is alive. 246 * 247 * The default behaviour is to continually dequeue events 248 * and make a subclass handle the event. 249 * 250 * Overridable by subclasses to customize event processing. 251 */ 252 protected void execute() { 253 try { 254 Event event = eventQueue_.dequeue(); 255 256 Logging.EventLoop_LOG.debug(owner() + " is processing " + event); 257 258 handle(event); 259 } catch (InterruptedException e) { 260 // If interrupted, we may be asked to stop 261 } 262 } 263 264 /** 265 * EventProcessor is a thread subclass whose primary goal is to keep a reference to the 266 * associated event loop, which is used to transform threads into the more manageable 267 * event loops 268 */ 269 public final class EventProcessor extends Thread { 270 271 protected EventProcessor(int stackSize) { 272 super(new ThreadGroup("EventLoopGroup"), null, "Event Loop", stackSize); 273 setName(toString()); 274 } 275 276 protected EventProcessor(){ 277 setName(toString()); 278 } 279 280 protected EventLoop serving() { return owner(); } 281 282 public final void run() { 283 while(!askedToStop_) { 284 execute(); 285 286 // give other event loops a chance to process an event 287 Thread.yield(); 288 } 289 } 290 291 public String toString() { 292 return "Event Loop " + serving().toString(); 293 } 294 295 } 296 297}