001/* An actor that delays the input for a certain amount of real time. 002 003 Copyright (c) 1998-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.actor.lib; 029 030import ptolemy.actor.parameters.PortParameter; 031import ptolemy.data.LongToken; 032import ptolemy.data.Token; 033import ptolemy.data.type.BaseType; 034import ptolemy.kernel.CompositeEntity; 035import ptolemy.kernel.Port; 036import ptolemy.kernel.util.IllegalActionException; 037import ptolemy.kernel.util.NameDuplicationException; 038import ptolemy.kernel.util.StringAttribute; 039import ptolemy.kernel.util.Workspace; 040 041/////////////////////////////////////////////////////////////////// 042//// Sleep 043 044/** 045 <p>On each firing, read at most one token from each input channel, sleep 046 by the specified amount of real time, and then produce the same input 047 tokens on the respective output channels. This actor calls Thread.sleep() 048 in the fire() method, so the thread that calls fire() will be suspended. 049 If fire() is called multiple times in one iteration, sleep is only called 050 the first time. 051 If the width of the output port is less than that of the input port, 052 the tokens in the extra channels are lost. 053 </p><p> 054 The effect of this actor is different in different domains. 055 In domains where all actors are iterated from within a single director 056 thread (like SDF and DE), then multiple instances of this actor will 057 result in cumulative time delays. That is, the time taken by an iteration 058 of the model will be greater than the sum of the sleep times of all the 059 instances. In domains where actors execute in their own thread (like PN 060 and CSP), only the execution of the individual actor is slowed. 061 Note that another way to slow down the execution of a model while running 062 inside vergil is to turn on animation.</p> 063 064 @author Jie Liu, Christopher Hylands, Edward A. Lee 065 @version $Id$ 066 @since Ptolemy II 1.0 067 068 @Pt.ProposedRating Green (eal) 069 @Pt.AcceptedRating Red (cxh) Review _wasSleepCalledInFireYet, esp locking 070 */ 071public class Sleep extends Transformer { 072 /** Construct an actor with the given container and name. 073 * @param container The container. 074 * @param name The name of this actor. 075 * @exception IllegalActionException If the actor cannot be contained 076 * by the proposed container. 077 * @exception NameDuplicationException If the container already has an 078 * actor with this name. 079 */ 080 public Sleep(CompositeEntity container, String name) 081 throws NameDuplicationException, IllegalActionException { 082 super(container, name); 083 sleepTime = new PortParameter(this, "sleepTime"); 084 sleepTime.setExpression("0L"); 085 sleepTime.setTypeEquals(BaseType.LONG); 086 087 Port sleepPort = sleepTime.getPort(); 088 StringAttribute sleepCardinal = new StringAttribute(sleepPort, 089 "_cardinal"); 090 sleepCardinal.setExpression("SOUTH"); 091 092 // Data type polymorphic, multiports. 093 input.setMultiport(true); 094 output.setMultiport(true); 095 output.setTypeAtLeast(input); 096 output.setWidthEquals(input, true); 097 } 098 099 /////////////////////////////////////////////////////////////////// 100 //// ports and parameters //// 101 102 /** The sleep time in milliseconds. This has type long and default 103 * "0L". 104 */ 105 public PortParameter sleepTime; 106 107 /////////////////////////////////////////////////////////////////// 108 //// public methods //// 109 110 /** Override the base class to set type constraints. 111 * @param workspace The workspace for the cloned object. 112 * @exception CloneNotSupportedException If cloned ports cannot have 113 * as their container the cloned entity (this should not occur), or 114 * if one of the attributes cannot be cloned. 115 * @return A new instance of Sleep. 116 */ 117 @Override 118 public Object clone(Workspace workspace) throws CloneNotSupportedException { 119 Sleep newObject = (Sleep) super.clone(workspace); 120 newObject.output.setTypeAtLeast(newObject.input); 121 newObject.output.setWidthEquals(newObject.input, true); 122 return newObject; 123 } 124 125 /** Read input tokens, call Thread.sleep(), and then 126 * transfer tokens from inputs to outputs, at most one token from each 127 * channel. If fire() is called twice in a row without an 128 * intervening call to either postfire() or prefire(), then no 129 * sleep is performed, an inputs are copied to the output immediately. 130 * <p> 131 * If the width of the output port is less than 132 * that of the input port, the tokens in the extra channels 133 * are lost. 134 * @exception IllegalActionException Not thrown in this base class 135 */ 136 @Override 137 public void fire() throws IllegalActionException { 138 if (!_wasSleepCalledInFireYet) { 139 _wasSleepCalledInFireYet = true; 140 super.fire(); 141 sleepTime.update(); 142 143 int inputWidth = input.getWidth(); 144 Token[] inputs = new Token[inputWidth]; 145 146 for (int i = 0; i < inputWidth; i++) { 147 if (input.hasToken(i)) { 148 inputs[i] = input.get(i); 149 } 150 } 151 152 try { 153 long sleepTimeValue = ((LongToken) sleepTime.getToken()) 154 .longValue(); 155 156 if (_debugging) { 157 _debug(getName() + ": Wait for " + sleepTimeValue 158 + " milliseconds."); 159 } 160 161 Thread.sleep(sleepTimeValue); 162 } catch (InterruptedException e) { 163 // Ignore... 164 } 165 166 int outputWidth = output.getWidth(); 167 168 for (int i = 0; i < inputWidth; i++) { 169 if (inputs[i] != null) { 170 if (i < outputWidth) { 171 output.send(i, inputs[i]); 172 } 173 } 174 } 175 } 176 } 177 178 /** Reset the flag that fire() checks so that fire() only sleeps once. 179 * @exception IllegalActionException If the base class throws it. 180 */ 181 @Override 182 public void initialize() throws IllegalActionException { 183 super.initialize(); 184 _wasSleepCalledInFireYet = false; 185 } 186 187 /** Reset the flag that fire() checks so that fire() only sleeps once. 188 * @exception IllegalActionException If the parent class throws it. 189 * @return Whatever the superclass returns (probably true). 190 */ 191 @Override 192 public boolean postfire() throws IllegalActionException { 193 _wasSleepCalledInFireYet = false; 194 return super.postfire(); 195 } 196 197 /////////////////////////////////////////////////////////////////// 198 //// private variables //// 199 200 /** True if sleep was called in fire(). Thread.sleep() should only 201 * be called once in fire(). 202 */ 203 private boolean _wasSleepCalledInFireYet = false; 204}