001/* A calendar queue implementation of the DE event queue. 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.domains.de.kernel; 029 030import ptolemy.actor.util.CQComparator; 031import ptolemy.actor.util.CalendarQueue; 032import ptolemy.actor.util.Time; 033import ptolemy.kernel.util.DebugListener; 034import ptolemy.kernel.util.InvalidStateException; 035 036/////////////////////////////////////////////////////////////////// 037//// DECQEventQueue 038 039/** 040 A calendar queue implementation of the DE event queue. 041 This queue stores DE events in the order of their timestamps, 042 microsteps, and then depths of their destination actors. See 043 {@link DEEventQueue} for more explanation of the order of DE events. 044 <P> 045 Its complexity is theoretically O(1) for both enqueue and dequeue 046 operations, assuming a reasonable distribution of timestamps. See 047 {@link ptolemy.actor.util.CalendarQueue}. 048 049 @author Lukito Muliadi, Edward A. Lee, Jie Liu, Haiyang Zheng 050 @version $Id$ 051 @since Ptolemy II 0.2 052 @Pt.ProposedRating Green (hyzheng) 053 @Pt.AcceptedRating Green (hyzheng) 054 */ 055public class DECQEventQueue implements DEEventQueue { 056 /** Construct an empty event queue. 057 */ 058 public DECQEventQueue() { 059 // Construct a calendar queue _cQueue with its default parameters: 060 // minBinCount is 2, binCountFactor is 2, and isAdaptive is true. 061 _cQueue = new CalendarQueue(new DECQComparator()); 062 } 063 064 /** Construct an empty event queue with the specified parameters. 065 * @param minBinCount The minimum number of bins. 066 * @param binCountFactor The factor when changing the bin count. 067 * @param isAdaptive If the queue changes its number of bins at run time. 068 */ 069 public DECQEventQueue(int minBinCount, int binCountFactor, 070 boolean isAdaptive) { 071 // Construct a calendar queue _cQueue with the given parameters. 072 _cQueue = new CalendarQueue(new DECQComparator(), minBinCount, 073 binCountFactor); 074 _cQueue.setAdaptive(isAdaptive); 075 } 076 077 /////////////////////////////////////////////////////////////////// 078 //// public methods //// 079 080 /** Append a listener to the current set of debug listeners. 081 * @param listener A listener to which to send debug messages. 082 * @see #removeDebugListener(DebugListener) 083 */ 084 @Override 085 public void addDebugListener(DebugListener listener) { 086 _cQueue.addDebugListener(listener); 087 } 088 089 /** Empty the event queue. This method is synchronized since there 090 * may be actors running under different threads in the DE domain. 091 */ 092 @Override 093 public void clear() { 094 _cQueue.clear(); 095 } 096 097 /** Return the earliest DE event in the queue without removing it 098 * from the queue. 099 * @return The earliest DE event in the queue. 100 * @exception InvalidStateException If the queue is empty. 101 */ 102 @Override 103 public final DEEvent get() { 104 return (DEEvent) _cQueue.get(); 105 } 106 107 /** Return true if this event queue is empty. 108 * @return True if there are no event in the queue. 109 */ 110 @Override 111 public final boolean isEmpty() { 112 return _cQueue.isEmpty(); 113 } 114 115 /** Put an event into the event queue. 116 * If the given DE event is not in the event queue, enqueue it 117 * into the event queue and notify all threads 118 * that are stalled waiting for a DE event to be put in the queue. 119 * This method is synchronized since there 120 * may be actors running under different threads in the DE domain. 121 * @param event The event to enqueue. 122 */ 123 @Override 124 public synchronized final void put(DEEvent event) { 125 if (!_cQueue.includes(event)) { 126 _cQueue.put(event); 127 notifyAll(); 128 } 129 } 130 131 /** Remove an event from the event queue and return true if 132 * it was removed, and false if it was not in the queue. 133 * This should only be used for pure events (consequences of 134 * fireAt()), not for events carrying payloads, because this 135 * does not remove the payload from the DEReceiver. 136 * The event passed is an argument need not be exactly the 137 * same event in the queue. It just has to match the 138 * actor, timeStamp, microstep, and depth of the event 139 * to be removed. 140 * @param event The event to enqueue. 141 * @return True If a match is found and the entry is removed. 142 */ 143 @Override 144 public synchronized final boolean remove(DEEvent event) { 145 return _cQueue.remove(event); 146 } 147 148 /** Unregister a debug listener. If the specified listener has not 149 * been previously registered, then do nothing. 150 * @param listener The listener to remove from the list of listeners 151 * to which debug messages are sent. 152 * @see #addDebugListener(DebugListener) 153 */ 154 @Override 155 public void removeDebugListener(DebugListener listener) { 156 _cQueue.removeDebugListener(listener); 157 } 158 159 /** Return the size of the event queue. 160 * @return The size of the event queue. 161 */ 162 @Override 163 public final int size() { 164 return _cQueue.size(); 165 } 166 167 /** Dequeue the earliest DE event in this event queue. 168 * @return The earliest DE event in the queue. 169 * @exception InvalidStateException If the queue is empty. 170 */ 171 @Override 172 public final DEEvent take() { 173 return (DEEvent) _cQueue.take(); 174 } 175 176 /** Return the events currently in the queue as an array. 177 * @return The events currently in the queue. 178 */ 179 @Override 180 public final Object[] toArray() { 181 return _cQueue.toArray(); 182 } 183 184 /** Describe the Contents of the queue as a string. 185 * @return A string with a comma-separated list of events. 186 */ 187 @Override 188 public String toString() { 189 Object[] array = toArray(); 190 StringBuffer buffer = new StringBuffer(""); 191 if (array != null) { 192 buffer.append("{"); 193 for (int i = 0; i < array.length; i++) { 194 if (i > 0) { 195 buffer.append(", "); 196 } 197 buffer.append(array[i]); 198 } 199 buffer.append("}"); 200 } 201 return buffer.toString(); 202 } 203 204 /////////////////////////////////////////////////////////////////// 205 //// private inner class //// 206 // An implementation of the CQComparator interface for use with 207 // calendar queue that compares two DEEvents according to their 208 // time stamps, microstep, and depth in that order. 209 // One DE event is said to be earlier than another, if it has 210 // a smaller time stamp, or when the time stamps are identical, 211 // it has a smaller microstep, or when both time stamps and 212 // microsteps are identical, it has a smaller depth. 213 // The default binWidth is 1.0, and the default zeroReference is 0.0. 214 private static class DECQComparator implements CQComparator { 215 216 // FindBugs suggests making this class static so as to decrease 217 // the size of instances and avoid dangling references. 218 219 /** Construct a new comparator. 220 */ 221 public DECQComparator() { 222 _binWidth = 1; 223 _zeroReference = 0.0; 224 } 225 226 /** Compare two arguments for order. Return a negative integer, 227 * zero, or a positive integer if the first argument is less than, 228 * equal to, or greater than the second. 229 * Both arguments must be instances of DEEvent. Otherwise a 230 * ClassCastException will be thrown. The compareTo() method 231 * of the first argument is used to do the comparison. 232 * 233 * @param object1 The first DE event. 234 * @param object2 The second DE event. 235 * @return A negative integer, zero, or a positive integer if the first 236 * argument is less than, equal to, or greater than the second. 237 * @exception ClassCastException If any of the arguments is not 238 * an instance of DEEvent. 239 */ 240 @Override 241 public final int compare(Object object1, Object object2) { 242 return ((DEEvent) object1).compareTo((DEEvent) object2); 243 } 244 245 /** Given a DE event, return the virtual index of the bin that 246 * should contain this event. If the argument is not an instance 247 * of DEEvent, then a ClassCastException will be thrown. 248 * If the bin number is larger than what can be represented 249 * in a long, then the low-order 64 bits will be returned. 250 * Note that this could change the sign of the result, but 251 * the way this is used in the CalendarQueue class, this is OK. 252 * It is converted to a bin number by masking some number of 253 * low-order bits, so the result will be unaffected by the 254 * sign error. 255 * Note that this method cannot return a long.MAX_VALUE, which 256 * is used internally by CalendarQueue class. 257 * @param event The event. 258 * @return The index of the virtual bin containing the event. 259 * @exception ClassCastException If the argument is not 260 * an instance of DEEvent. 261 */ 262 @Override 263 public final long getVirtualBinNumber(Object event) { 264 // Note: The longValue() method will only 265 // returns the low-order 64 bits of the result. 266 // If it is larger than what can be represented 267 // in 64 bits, then the returned result will be wrapped. 268 long value = (long) (((DEEvent) event).timeStamp() 269 .subtract(_zeroReference).getLongValue() / _binWidth); 270 if (value != Long.MAX_VALUE) { 271 return value; 272 } else { 273 return Long.MAX_VALUE - 1; 274 } 275 } 276 277 /** Given an array of DE events, set an appropriate bin width. 278 * This method assumes that no two DE events have the same time stamp, 279 * microstep, and depth. This method also assumes that the events 280 * are sorted in a time-increasing order. {@link DEEventQueue}. 281 * Note that even the time stamps may not be increasing, 282 * the receiver depths, or the microsteps may be increasing. 283 * This method attempts to choose the bin width so that 284 * the average number of entries in a bin is one. 285 * If the argument is null or is an array with length less 286 * than two, set the bin width to the default, which is 1.0 287 * for this implementation. 288 * 289 * @param entryArray An array of DE events. 290 * @exception ClassCastException If any entry in the array is not 291 * an instance of DEEvent. 292 */ 293 @Override 294 public void setBinWidth(Object[] entryArray) { 295 if (entryArray == null || entryArray.length < 2) { 296 // Reset to default. 297 _binWidth = 1; 298 _zeroReference = 0.0; 299 return; 300 } 301 302 double[] diff = new double[entryArray.length - 1]; 303 Time firstEntryTime = ((DEEvent) entryArray[0]).timeStamp(); 304 Time lastEntryTime = ((DEEvent) entryArray[entryArray.length - 1]) 305 .timeStamp(); 306 307 if (firstEntryTime.isInfinite() 308 && firstEntryTime.equals(lastEntryTime)) { 309 // To avoid setting NaN or 0.0 310 // for the width, apparently due to simultaneous events, 311 // we leave it unchanged instead. 312 return; 313 } 314 315 double average = lastEntryTime.subtract(firstEntryTime) 316 .getDoubleValue(); 317 average = average / (entryArray.length - 1); 318 319 double effectiveAverage = 0; 320 int effectiveSamples = 0; 321 322 if (Double.isInfinite(average)) { 323 return; 324 } 325 326 for (int i = 0; i < entryArray.length - 1; ++i) { 327 diff[i] = ((DEEvent) entryArray[i + 1]).timeStamp() 328 .subtract(((DEEvent) entryArray[i]).timeStamp()) 329 .getDoubleValue(); 330 if (diff[i] < 2 * average) { 331 effectiveSamples++; 332 effectiveAverage = effectiveAverage + diff[i]; 333 } 334 } 335 336 if (effectiveAverage == 0 || effectiveSamples == 0) { 337 // To avoid setting NaN or 0.0 338 // for the width, apparently due to simultaneous events, 339 // we leave it unchanged instead. 340 return; 341 } 342 343 effectiveAverage = effectiveAverage / effectiveSamples; 344 _binWidth = effectiveAverage * 3; 345 } 346 347 /** Set the zero reference, to be used in calculating the virtual 348 * bin number. The argument should be a DE event, otherwise a 349 * ClassCastException will be thrown. 350 * @exception ClassCastException If the argument is not an instance 351 * of DEEvent. 352 */ 353 @Override 354 public void setZeroReference(Object zeroReference) { 355 _zeroReference = ((DEEvent) zeroReference).timeStamp() 356 .getDoubleValue(); 357 } 358 359 /////////////////////////////////////////////////////////////////// 360 //// private members //// 361 // The bin width. 362 private double _binWidth = 1.0; 363 364 // The zero reference. 365 private double _zeroReference = 0.0; 366 } 367 368 /////////////////////////////////////////////////////////////////// 369 //// private variables //// 370 // An instance of CalendarQueue used for sorting and storing events. 371 private CalendarQueue _cQueue; 372}