/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/signals/natives/NATSignal.java

http://ambienttalk.googlecode.com/ · Java · 260 lines · 131 code · 50 blank · 79 comment · 14 complexity · d34b339d73c347e1137f2726f868e318 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * NATSignal.java created on Apr 14, 2008
  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.natives;
  29. import java.lang.reflect.Method;
  30. import java.util.HashSet;
  31. import java.util.Iterator;
  32. import edu.vub.at.actors.natives.ELActor;
  33. import edu.vub.at.eval.Evaluator;
  34. import edu.vub.at.exceptions.InterpreterException;
  35. import edu.vub.at.exceptions.XIllegalOperation;
  36. import edu.vub.at.objects.ATMessage;
  37. import edu.vub.at.objects.ATNil;
  38. import edu.vub.at.objects.ATNumber;
  39. import edu.vub.at.objects.ATObject;
  40. import edu.vub.at.objects.ATTable;
  41. import edu.vub.at.objects.natives.NATMethodInvocation;
  42. import edu.vub.at.objects.natives.NATNumber;
  43. import edu.vub.at.objects.natives.NATTable;
  44. import edu.vub.at.objects.natives.NativeATObject;
  45. import edu.vub.at.signals.ATSignal;
  46. import edu.vub.at.signals.SignalListener;
  47. import edu.vub.at.signals.eval.DataflowEngine;
  48. import edu.vub.at.signals.eval.EncapsulatingSignal;
  49. import edu.vub.at.util.logging.Logging;
  50. /**
  51. * NATSignal is the abstract root class of all AmbientTalk signals. It implements the complete
  52. * SignalListener interface and provides the required support to ensure that events originating
  53. * from a (primitive) event source outside of AmbientTalk's event loop are properly translated
  54. * into asynchronous messages.
  55. *
  56. * Additionally, the signal keeps track of the context in which it was created and ensures that
  57. * if it was created in a reactive activation, it is properly unhooked before the activation is
  58. * triggered anew.
  59. *
  60. * @author smostinc
  61. */
  62. public abstract class NATSignal extends NativeATObject implements ATSignal, SignalListener {
  63. private int itsDependencyHeight;
  64. private boolean hasBeenUnhooked;
  65. private ELActor itsOwner;
  66. protected HashSet itsDependents = new HashSet();
  67. protected final ATObject itsConsumer;
  68. protected NATSignal(int initialDependencyHeight) {
  69. this(initialDependencyHeight, null);
  70. }
  71. protected NATSignal(int initialDependencyHeight, ATObject theConsumer) {
  72. // 1) Set the object that will consume all incoming signal updates
  73. itsConsumer = theConsumer;
  74. // 2) If this listener is created in the context of a reactive activation, the height
  75. // of the encapsulating activation should be taken into account, when determining the
  76. // stratum in which this signal listener should be allocated.
  77. DataflowEngine theEngine = Evaluator.getDataflowEngine();
  78. EncapsulatingSignal theEncapsulator = theEngine.getEncapsulatingSignal();
  79. int theEncapsulatorsDependencyHeight = theEncapsulator!=null ? theEncapsulator.dependencyHeight() : 0;
  80. itsDependencyHeight = Math.max(initialDependencyHeight, theEncapsulatorsDependencyHeight);
  81. // 3) If this listener is created in the context of a reactive activation, updates to
  82. // that activation imply that this listener becomes invalid and should be unhooked.
  83. hasBeenUnhooked = false;
  84. if(theEncapsulator != null) theEncapsulator.registerDependency(this);
  85. // 4) To properly synchronize signal updates, external event sources should notify us
  86. // of updates using asynchronous message sends. To distinguish between internal and
  87. // and external event sources, we keep track of the actor that created the listener.
  88. // Updates originating from another process can then be considered external.
  89. itsOwner = ELActor.currentActor();
  90. }
  91. public int dependencyHeight() {
  92. return itsDependencyHeight;
  93. }
  94. public ATNumber meta_dependencyHeight() {
  95. return NATNumber.atValue(itsDependencyHeight);
  96. }
  97. public void updateDependencyHeight(int newDependencyHeight) {
  98. Logging.ReactiveProgramming_LOG.debug("Updating stratum of signal: " + this + " from: " + dependencyHeight() + " to: " + newDependencyHeight);
  99. // Conceptually, updating the height could be as simple as this :
  100. // itsDependencyHeight = Math.max(itsDependencyHeight, newDependencyHeight);
  101. // However, it is important to ensure that if this listener has been enqueued for
  102. // recomputation, that it occurs in the correct stratum to avoid update glitches.
  103. if(newDependencyHeight > itsDependencyHeight) {
  104. itsDependencyHeight = newDependencyHeight;
  105. DataflowEngine theEngine = Evaluator.getDataflowEngine();
  106. // This ensures that the updated dependency height is reflected in the queue
  107. // that is kept by the dataflow engine.
  108. theEngine.reprioritize(this);
  109. // Notify all dependent signals
  110. for(Iterator dependentComputationIt = itsDependents.iterator();
  111. dependentComputationIt.hasNext();) {
  112. SignalListener currentDependentComputation = (SignalListener) dependentComputationIt.next();
  113. // The new height of all dependent computations should at least be our own
  114. // dependency height plus one.
  115. currentDependentComputation.updateDependencyHeight(itsDependencyHeight + 1);
  116. }
  117. }
  118. }
  119. // Dependency Management
  120. public ATNil meta_addDependent(SignalListener dependent) throws InterpreterException {
  121. if(dependent.dependencyHeight() > this.dependencyHeight()) {
  122. itsDependents.add(dependent);
  123. } else {
  124. throw new XIllegalOperation("Attempted to add a dependent signal with a lower dependency height than my own");
  125. }
  126. return Evaluator.getNil();
  127. }
  128. public ATNil meta_removeDependent(SignalListener dependent) throws InterpreterException {
  129. itsDependents.remove(dependent);
  130. return Evaluator.getNil();
  131. }
  132. public void unhook() {
  133. hasBeenUnhooked = true;
  134. itsDependents.clear();
  135. }
  136. // Update Handling
  137. /**
  138. * This method is declared final, since subclasses should override the protected method
  139. * inner_scheduleUpdate instead of overriding this method directly.
  140. */
  141. public final void scheduleUpdate(ATSignal theParent, ATMessage theUpdate) {
  142. if( !shouldSynchronize(theParent, theUpdate))
  143. inner_scheduleUpdate(theParent, theUpdate);
  144. }
  145. protected void inner_scheduleUpdate(ATSignal theParent, ATMessage theUpdate) {
  146. DataflowEngine theEngine = Evaluator.getDataflowEngine();
  147. theEngine.schedulePendingUpdate(theParent, this, theUpdate);
  148. }
  149. /**
  150. * This method is declared final, since subclasses should override the protected method
  151. * inner_performUpdate instead of overriding this method directly.
  152. */
  153. public final boolean performUpdate(ATSignal theParent, ATMessage theUpdate) {
  154. if(!hasBeenUnhooked)
  155. inner_performUpdate(theParent, theUpdate);
  156. return !hasBeenUnhooked;
  157. }
  158. protected void inner_performUpdate(ATSignal theParent, ATMessage theUpdate) {
  159. try {
  160. if(itsConsumer == null) {
  161. propagateNewMessage(theUpdate);
  162. } else {
  163. theUpdate.base_sendTo(itsConsumer, theParent);
  164. }
  165. } catch (InterpreterException e) {
  166. // Could not send the message succesfully
  167. e.printStackTrace();
  168. }
  169. };
  170. // Auxiliary Methods
  171. /**
  172. * This method tests whether the signal update, that has to be scheduled, originates from
  173. * another process. If the update is external, this method will schedule an asynchronous
  174. * message in the owning actors event loop.
  175. */
  176. private boolean shouldSynchronize(ATSignal theParent, ATMessage theUpdate) {
  177. try {
  178. // Update is called from an external Thread : Synchronization is needed here
  179. if(Thread.currentThread() != itsOwner.processor()) {
  180. // Get the method to schedule a signal update
  181. Method update = SignalListener.class.getDeclaredMethod(
  182. "scheduleUpdate", new Class[] { ATSignal.class, ATMessage.class });
  183. // And schedule it for execution within the owning event loop
  184. itsOwner.event_symbioticInvocation(this,
  185. update, new ATObject[] { theParent, theUpdate });
  186. return true;
  187. }
  188. } catch (Exception e) {
  189. // Could not select the method from this object;
  190. e.printStackTrace();
  191. }
  192. return false;
  193. }
  194. protected void propagateNewMessage(ATMessage updateMessage) {
  195. for(Iterator dependentComputationIt = itsDependents.iterator();
  196. dependentComputationIt.hasNext();) {
  197. SignalListener currentDependentComputation = (SignalListener) dependentComputationIt.next();
  198. currentDependentComputation.scheduleUpdate(this, updateMessage);
  199. }
  200. }
  201. protected void propagateNewValues(ATTable computedValues) throws InterpreterException {
  202. propagateNewMessage(new NATMethodInvocation(
  203. Evaluator._APPLY_,
  204. NATTable.of(computedValues),
  205. NATTable.EMPTY));
  206. }
  207. protected void propagateNewValue(ATObject computedValue) throws InterpreterException {
  208. propagateNewValues(NATTable.of(computedValue));
  209. }
  210. }