001/* A Scheduler infrastructure for the SDF domain 002 003 Copyright (c) 2003-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.domains.sdf.kernel; 029 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033 034import ptolemy.actor.CompositeActor; 035import ptolemy.actor.Director; 036import ptolemy.actor.IOPort; 037import ptolemy.actor.sched.NotSchedulableException; 038import ptolemy.actor.sched.Scheduler; 039import ptolemy.actor.util.ConstVariableModelAnalysis; 040import ptolemy.actor.util.DFUtilities; 041import ptolemy.actor.util.DependencyDeclaration; 042import ptolemy.data.expr.Variable; 043import ptolemy.kernel.Entity; 044import ptolemy.kernel.Port; 045import ptolemy.kernel.Relation; 046import ptolemy.kernel.util.ChangeRequest; 047import ptolemy.kernel.util.IllegalActionException; 048import ptolemy.kernel.util.InternalErrorException; 049import ptolemy.kernel.util.KernelException; 050import ptolemy.kernel.util.NameDuplicationException; 051import ptolemy.kernel.util.Workspace; 052 053/////////////////////////////////////////////////////////////////// 054//// BaseSDFScheduler 055 056/** 057 This class factors code out of the SDF domain, for use in different 058 schedulers, so that they can be implemented in a consistent fashion. 059 060 @author Stephen Neuendorffer, Shuvra S. Bhattacharyya 061 @version $Id$ 062 @since Ptolemy II 4.0 063 @Pt.ProposedRating Red (neuendor) 064 @Pt.AcceptedRating Red (neuendor) 065 */ 066public abstract class BaseSDFScheduler extends Scheduler { 067 /** Construct a scheduler with no container(director) 068 * in the default workspace, the name of the scheduler is 069 * "Scheduler". 070 */ 071 public BaseSDFScheduler() { 072 super(); 073 } 074 075 /** Construct a scheduler in the given workspace with the name 076 * "Scheduler". 077 * If the workspace argument is null, use the default workspace. 078 * The scheduler is added to the list of objects in the workspace. 079 * Increment the version number of the workspace. 080 * 081 * @param workspace Object for synchronization and version tracking. 082 */ 083 public BaseSDFScheduler(Workspace workspace) { 084 super(workspace); 085 } 086 087 /** Construct a scheduler in the given container with the given name. 088 * The container argument must not be null, or a 089 * NullPointerException will be thrown. This attribute will use the 090 * workspace of the container for synchronization and version counts. 091 * If the name argument is null, then the name is set to the empty string. 092 * Increment the version of the workspace. 093 * @param container The container. 094 * @param name The name of this attribute. 095 * @exception IllegalActionException If the attribute is not of an 096 * acceptable class for the container, or if the name contains a period. 097 * @exception NameDuplicationException If the name coincides with 098 * an attribute already in the container. 099 */ 100 public BaseSDFScheduler(Director container, String name) 101 throws IllegalActionException, NameDuplicationException { 102 super(container, name); 103 } 104 105 /////////////////////////////////////////////////////////////////// 106 //// public methods //// 107 108 /** Declare the rate dependency on any external ports of the model. 109 * SDF directors should invoke this method once during preinitialize. 110 * @exception IllegalActionException If there is a problem setting 111 * the rate dependency on an external port. 112 */ 113 public abstract void declareRateDependency() throws IllegalActionException; 114 115 /////////////////////////////////////////////////////////////////// 116 //// protected methods //// 117 118 /** Add a DependencyDeclaration (with the name 119 * "_SDFRateDependencyDeclaration") to the variable with the given 120 * name in the given port that declares the variable is dependent 121 * on the given list of variables. If a dependency declaration 122 * with that name already exists, then simply set its dependents 123 * list to the given list. 124 * @param analysis The ConstVariableModelAnalysis 125 * @param port The port that gets the DependencyDeclaration. 126 * @param name The name of the DependencyDeclaration. 127 * @param dependents The dependents. 128 * @exception IllegalActionException If there is a problem setting 129 * the rate dependency on a port 130 */ 131 @SuppressWarnings("unused") 132 protected void _declareDependency(ConstVariableModelAnalysis analysis, 133 Port port, String name, List dependents) 134 throws IllegalActionException { 135 if (_debugging && VERBOSE) { 136 _debug("declaring dependency for rate variable " + name 137 + " in port " + port.getFullName()); 138 } 139 140 Variable variable = DFUtilities.getRateVariable(port, name); 141 DependencyDeclaration declaration = (DependencyDeclaration) variable 142 .getAttribute("_SDFRateDependencyDeclaration", 143 DependencyDeclaration.class); 144 145 if (declaration == null) { 146 try { 147 declaration = new DependencyDeclaration(variable, 148 "_SDFRateDependencyDeclaration"); 149 } catch (NameDuplicationException ex) { 150 // We used to ignore this, but FindBugs would complain 151 // that declaration could still be null. 152 throw new InternalErrorException("Failed to construct " 153 + "_SDFRateDependencyDeclaration"); 154 } 155 } 156 157 declaration.setDependents(dependents); 158 analysis.addDependencyDeclaration(declaration); 159 } 160 161 /** Create and set a parameter in each relation according 162 * to the buffer sizes calculated for this system. 163 * @param minimumBufferSizes A map from relation 164 * to the minimum possible buffer size of that relation. 165 */ 166 protected void _saveBufferSizes(final Map minimumBufferSizes) { 167 Director director = (Director) getContainer(); 168 final CompositeActor container = (CompositeActor) director 169 .getContainer(); 170 171 // FIXME: These buffer sizes should be properties of input ports, 172 // not properties of relations. 173 ChangeRequest request = new ChangeRequest(this, "Record buffer sizes") { 174 @Override 175 protected void _execute() throws KernelException { 176 Iterator relations = container.relationList().iterator(); 177 178 while (relations.hasNext()) { 179 Relation relation = (Relation) relations.next(); 180 Object bufferSizeObject = minimumBufferSizes.get(relation); 181 182 if (bufferSizeObject instanceof Integer) { 183 int bufferSize = ((Integer) bufferSizeObject) 184 .intValue(); 185 DFUtilities.setOrCreate(relation, "bufferSize", 186 bufferSize); 187 188 if (_debugging) { 189 _debug("Adding bufferSize parameter to " 190 + relation.getName() + " with value " 191 + bufferSize); 192 } 193 } else if (bufferSizeObject instanceof String) { 194 String bufferSizeExpression = (String) bufferSizeObject; 195 DFUtilities.setOrCreate(relation, "bufferSize", 196 "\"" + bufferSizeExpression + "\""); 197 198 if (_debugging) { 199 _debug("Adding bufferSize parameter to " 200 + relation.getName() + " with expression " 201 + bufferSizeExpression); 202 } 203 } else if (bufferSizeObject == null) { 204 } else { 205 throw new InternalErrorException("Invalid value found " 206 + "in buffer size map.\nValue is of type " 207 + bufferSizeObject.getClass().getName() 208 + ".\nIt should be of type Integer or String.\n"); 209 } 210 } 211 } 212 }; 213 214 // Indicate that the change is non-persistent, so that 215 // the UI doesn't prompt to save. 216 request.setPersistent(false); 217 container.requestChange(request); 218 } 219 220 /** Push the rates calculated for this system up to the contained Actor, 221 * but only if the ports do not have a set rates. 222 * This allows the container to be properly scheduled if it is 223 * in a hierarchical system and the outside system is SDF. 224 * @param externalRates A map from external port to the rate of that 225 * port. 226 * @exception IllegalActionException If any called method throws it. 227 * @exception NotSchedulableException If an external port is both 228 * an input and an output, or neither an input or an output, or 229 * connected on the inside to ports that have different 230 * tokenInitProduction. 231 */ 232 @SuppressWarnings("unused") 233 protected void _saveContainerRates(Map externalRates) 234 throws NotSchedulableException, IllegalActionException { 235 Director director = (Director) getContainer(); 236 CompositeActor container = (CompositeActor) director.getContainer(); 237 Iterator ports = container.portList().iterator(); 238 239 while (ports.hasNext()) { 240 IOPort port = (IOPort) ports.next(); 241 242 if (_debugging && VERBOSE) { 243 _debug("External Port " + port.getName()); 244 } 245 246 Integer rate = (Integer) externalRates.get(port); 247 248 if (port.isInput() && port.isOutput()) { 249 throw new NotSchedulableException(port, 250 "External port is both an input and an output, " 251 + "which is not allowed in SDF."); 252 } else if (port.isInput()) { 253 DFUtilities.setIfNotDefined(port, "tokenConsumptionRate", 254 rate.intValue()); 255 256 if (_debugging && VERBOSE) { 257 _debug("Setting tokenConsumptionRate to " 258 + rate.intValue()); 259 } 260 261 // External ports do not any initial consumption tokens 262 // that are caused by the inside model, so we set this 263 // parameter to zero. 264 DFUtilities.setIfNotDefined(port, "tokenInitConsumption", 0); 265 266 if (_debugging && VERBOSE) { 267 _debug("Setting tokenInitConsumption to 0."); 268 } 269 } else if (port.isOutput()) { 270 DFUtilities.setIfNotDefined(port, "tokenProductionRate", 271 rate.intValue()); 272 273 if (_debugging && VERBOSE) { 274 _debug("Setting tokenProductionRate to " + rate.intValue()); 275 } 276 277 // Infer init production. 278 // Note that this is a very simple type of inference... 279 // However, in general, we don't want to try to 280 // flatten this model... 281 Iterator connectedPorts = port.insideSourcePortList() 282 .iterator(); 283 IOPort foundOutputPort = null; 284 int inferredRate = 0; 285 286 while (connectedPorts.hasNext()) { 287 IOPort connectedPort = (IOPort) connectedPorts.next(); 288 289 int newRate; 290 291 if (connectedPort.isOutput()) { 292 newRate = DFUtilities 293 .getTokenInitProduction(connectedPort); 294 } else { 295 newRate = 0; 296 } 297 298 // If we've already set the rate, then check that the 299 // rate for any other internal port is correct. 300 if (foundOutputPort != null && newRate != inferredRate) { 301 throw new NotSchedulableException(port, 302 "External output port " + port 303 + " is connected on the inside to ports " 304 + "with different initial production: " 305 + foundOutputPort + " and " 306 + connectedPort); 307 } 308 309 foundOutputPort = connectedPort; 310 inferredRate = newRate; 311 } 312 313 // If this output port has had its tokenInitConsumption 314 // parameter set to something other than zero, the this 315 // means that it will receive a token on the inside from 316 // some port that does initial production, such as PublisherPort. 317 // These initial tokens become initial _production_ for this 318 // port. 319 int initConsumption = DFUtilities.getTokenInitConsumption(port); 320 inferredRate += initConsumption; 321 322 DFUtilities.setIfNotDefined(port, "tokenInitProduction", 323 inferredRate); 324 325 if (_debugging && VERBOSE) { 326 _debug("Setting tokenInitProduction to " + inferredRate); 327 } 328 } else { 329 throw new NotSchedulableException(port, 330 "External port is neither an input and an output, " 331 + "which is not allowed in SDF."); 332 } 333 } 334 } 335 336 /** Create and set a parameter in each actor according 337 * to the number of times it will fire in one execution of the schedule. 338 * @param entityToFiringsPerIteration A map from actor to firing count. 339 */ 340 protected void _saveFiringCounts(final Map entityToFiringsPerIteration) { 341 Director director = (Director) getContainer(); 342 final CompositeActor container = (CompositeActor) director 343 .getContainer(); 344 345 ChangeRequest request = new ChangeRequest(this, 346 "Record firings per iteration") { 347 @Override 348 protected void _execute() throws KernelException { 349 Iterator entities = entityToFiringsPerIteration.keySet() 350 .iterator(); 351 352 while (entities.hasNext()) { 353 Entity entity = (Entity) entities.next(); 354 int firingCount = ((Integer) entityToFiringsPerIteration 355 .get(entity)).intValue(); 356 DFUtilities.setOrCreate(entity, "firingsPerIteration", 357 firingCount); 358 359 if (_debugging) { 360 _debug("Adding firingsPerIteration parameter to " 361 + entity.getName() + " with value " 362 + firingCount); 363 } 364 } 365 } 366 }; 367 368 // Indicate that the change is non-persistent, so that 369 // the UI doesn't prompt to save. 370 request.setPersistent(false); 371 container.requestChange(request); 372 } 373 374 /////////////////////////////////////////////////////////////////// 375 //// protected variables //// 376 377 /** If true, then print verbose messages. By default, this variable 378 * is set to false. To enable verbose messages, edit the source file 379 * and recompile. 380 */ 381 protected static final boolean VERBOSE = false; 382}