PageRenderTime 31ms CodeModel.GetById 18ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist220411/src/edu/vub/at/actors/eventloops/EventLoop.java

http://ambienttalk.googlecode.com/
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}