/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/signals/eval/DataflowEngine.java

http://ambienttalk.googlecode.com/ · Java · 199 lines · 85 code · 34 blank · 80 comment · 8 complexity · 8a940fc4d7391bd0f5f4f055f797f969 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * DataflowEngine.java created on Jun 14, 2008 at 6:48:44 PM
  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. */
  28. package edu.vub.at.signals.eval;
  29. import java.util.Enumeration;
  30. import com.objectspace.jgl.BinaryPredicate;
  31. import com.objectspace.jgl.Pair;
  32. import com.objectspace.jgl.PriorityQueue;
  33. import com.objectspace.jgl.Stack;
  34. import edu.vub.at.exceptions.InterpreterException;
  35. import edu.vub.at.objects.ATMessage;
  36. import edu.vub.at.signals.ATSignal;
  37. import edu.vub.at.signals.SignalListener;
  38. import edu.vub.at.util.logging.Logging;
  39. /**
  40. * The dataflow engine is responsible for the propagation of signal updates to all dependent
  41. * computations. It interacts with the event loop actor in which it is run, which calls the
  42. * propagatePendingUpdates method of the engine at the end of each event loop turn.
  43. * <p>
  44. *
  45. * @author smostinc
  46. *
  47. */
  48. public class DataflowEngine {
  49. /**
  50. * When a new signal is created in the context of a dependent computation, its value is
  51. * not computed immediately since some of the signals on which it depends may already be
  52. * updated, while others still contain stale information. In order to allow the dataflow
  53. * engine to avoid incorrect updates, these dependent signals register that they need to
  54. * be initialized. This is achieved internally by requesting the _INITIALIZER_ object to
  55. * send them a default message.
  56. */
  57. /**
  58. * A prioritized queue of SignalListeners which need to be updated. The listeners are
  59. * sorted according to their dependency height, to avoid update glitches.
  60. */
  61. private PriorityQueue theSignalsToBeUpdated = new PriorityQueue(new BinaryPredicate() {
  62. public boolean execute(Object first, Object second) {
  63. Pair firstPair = (Pair) first;
  64. Pair secondPair = (Pair) second;
  65. SignalListener firstL = (SignalListener) firstPair.first;
  66. SignalListener secondL = (SignalListener) secondPair.first;
  67. return (firstL.dependencyHeight() > secondL.dependencyHeight());
  68. }
  69. });
  70. private int currentlyReachedStratum = 0;
  71. public void propagatePendingUpdates() {
  72. while(theSignalsToBeUpdated.size() != 0) {
  73. // Previously propagated updates may have caused signals to update their height.
  74. if(should_reprioritize) do_reprioritize();
  75. Pair theUpdateTuple = (Pair) theSignalsToBeUpdated.pop();
  76. SignalListener listener = (SignalListener) theUpdateTuple.first;
  77. Pair theMessageTuple = (Pair) theUpdateTuple.second;
  78. ATSignal propagator = (ATSignal) theMessageTuple.first;
  79. ATMessage message = (ATMessage) theMessageTuple.second;
  80. Logging.ReactiveProgramming_LOG.debug("Performing update: " + message + " from: " + propagator + " to: " + listener + " (" + theSignalsToBeUpdated.size() + " others remaining)");
  81. assert(currentlyReachedStratum <= listener.dependencyHeight(), "Dataflow engine asked to lower the stratum currently being served.");
  82. currentlyReachedStratum = listener.dependencyHeight();
  83. if(! listener.performUpdate(propagator, message))
  84. try {
  85. propagator.meta_removeDependent(listener);
  86. } catch (InterpreterException e) {
  87. // TODO Auto-generated catch block
  88. e.printStackTrace();
  89. }
  90. }
  91. currentlyReachedStratum = 0;
  92. }
  93. public void schedulePendingUpdate(ATSignal thePropagator, SignalListener theDependent, ATMessage theUpdate) {
  94. Logging.ReactiveProgramming_LOG.debug("Signal: " + thePropagator + " schedules update: " + theUpdate + " for: " + theDependent);
  95. assert(thePropagator.dependencyHeight() >= currentlyReachedStratum, "Signal schedules update after the dataflow engine has reached a higher stratum.");
  96. assert(theDependent.dependencyHeight() > currentlyReachedStratum, "Signal update scheduled with lower or equal stratum than the one currently being served");
  97. theSignalsToBeUpdated.add(new Pair(theDependent, new Pair(thePropagator, theUpdate)));
  98. }
  99. /**
  100. * This method is called when the stratum in which a signal listener is allocated has to
  101. * change. It ensures that the priority queue of the dataflow engine will eventually be
  102. * updated to reflect the correct heights. This update is guaranteed to occur before the
  103. * next scheduled update is performed.
  104. *
  105. * @param theListener
  106. */
  107. public void reprioritize(SignalListener theListener) {
  108. should_reprioritize = true;
  109. }
  110. /*
  111. * Flag indicating that the queue should be sorted before proceeding with the propagation
  112. * of signal updates.
  113. */
  114. private boolean should_reprioritize = false;
  115. /*
  116. * Auxiliary method which is called when the dataflow engine detects that the dependency
  117. * height of one of more signals has changed. The resorting of the priority queue doesn't
  118. * occur immediately as dependency height updates tend to cascade.
  119. */
  120. private void do_reprioritize() {
  121. PriorityQueue theReprioritizedQueue = new PriorityQueue(theSignalsToBeUpdated.getComparator());
  122. for(Enumeration pairs = theSignalsToBeUpdated.elements();
  123. pairs.hasMoreElements();) {
  124. theReprioritizedQueue.add(pairs.nextElement());
  125. }
  126. theSignalsToBeUpdated = theReprioritizedQueue;
  127. should_reprioritize = false;
  128. }
  129. /**
  130. * theSurroundingSignal implements a dynamic variable to keep track of whether dataflow
  131. * graph elements are constructed in the dynamic extent of a method with a time-varying
  132. * receiver or argument. If this is the case, the constructed network implicitly depends
  133. * on the execution of the method. In other words, if the method is triggered again, the
  134. * network needs to be deconstructed.
  135. * <p>
  136. * This method allows the interpreter to keep track of such implicit dependencies, such
  137. * that when the method is triggered again, the dataflow graph which implicitly depends
  138. * on it is deconstructed. To aid in the deconstruction, the dataflow nodes that need to
  139. * be removed should be in a higher strata such that they have not computed yet when the
  140. * method is triggered.
  141. */
  142. private final Stack theSurroundingSignal = new Stack();
  143. public EncapsulatingSignal getEncapsulatingSignal() {
  144. if(theSurroundingSignal.size() == 0) return null;
  145. return (EncapsulatingSignal) theSurroundingSignal.top();
  146. }
  147. public void setEncapsulatingSignal(EncapsulatingSignal theSignal) {
  148. theSurroundingSignal.push(theSignal);
  149. }
  150. public void resetEncapsulatingSignal() {
  151. theSurroundingSignal.pop();
  152. }
  153. public int getCurrentlyReachedStratum() {
  154. return currentlyReachedStratum;
  155. }
  156. public boolean hasPendingUpdates() {
  157. return theSignalsToBeUpdated.size() != 0;
  158. }
  159. private void assert(boolean condition, String message) {
  160. if(! condition) {
  161. Logging.ReactiveProgramming_LOG.fatal(message);
  162. throw new RuntimeException(message);
  163. }
  164. }
  165. }