MATSIM
AbstractQNetsimEngineRunner.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * QSimEngineRunner.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2009 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.core.mobsim.qsim.qnetsimengine;
22 
23 import java.util.Iterator;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.ListIterator;
27 import java.util.Queue;
28 import java.util.concurrent.ConcurrentLinkedQueue;
29 
30 import org.matsim.core.mobsim.qsim.QSim;
31 
39 abstract class AbstractQNetsimEngineRunner extends NetElementActivationRegistry {
40 
41  private double time = 0.0;
42 
43  /*
44  * This needs to be thread-safe since QNodes could be activated concurrently
45  * from multiple threads. In previous implementations, this data structure was
46  * a Map since it was possible that the same node was activated concurrently.
47  * Now, the implementation of the QNode was adapted in a way that this is not
48  * possible anymore.
49  * cdobler, sep'14
50  */
51  private final Queue<QNodeI> nodesQueue = new ConcurrentLinkedQueue<>();
52 
53  /*
54  * Needs not to be thread-safe since links are only activated from nodes which
55  * are handled (by design) from links handled by the same thread. Therefore,
56  * no concurrent add operation can occur.
57  * cdobler, sep'14
58  */
59  private final List<QLinkI> linksList = new LinkedList<>();
60 
61  /*
62  * Ensure that nodes and links are only activate during times where we expect it.
63  * Otherwise this could result in unpredictable behavior. Therefore we throw
64  * an exception then.
65  * Doing so allows us adding nodes and links directly to the nodesQueue respectively
66  * the linksList. Previously, we had to cache them in other data structures and copy
67  * them at a later point in time.
68  * cdobler, sep'14
69  */
70  private boolean lockNodes = false;
71  private boolean lockLinks = false;
72 
73  /*package*/ long[] runTimes;
74  private long startTime = 0;
75  {
76  if (QSim.analyzeRunTimes) runTimes = new long[QNetsimEngineWithThreadpool.numObservedTimeSteps];
77  else runTimes = null;
78  }
79 
80  /*package*/ final void setTime(final double t) {
81  time = t;
82  }
83 
84  public abstract void afterSim() ;
85 
86  protected void moveNodes() {
87  boolean remainsActive;
88  this.lockNodes = true;
89  QNodeI node;
90  Iterator<QNodeI> simNodes = this.nodesQueue.iterator();
91  while (simNodes.hasNext()) {
92  node = simNodes.next();
93  remainsActive = node.doSimStep(time);
94  if (!remainsActive) simNodes.remove();
95  }
96  this.lockNodes = false;
97  }
98 
99  protected final void moveLinks() {
100  boolean remainsActive;
101  lockLinks = true;
102  QLinkI link;
103  ListIterator<QLinkI> simLinks = this.linksList.listIterator();
104  while (simLinks.hasNext()) {
105  link = simLinks.next();
106 
107  remainsActive = link.doSimStep();
108 
109  if (!remainsActive) simLinks.remove();
110  }
111  lockLinks = false;
112  }
113 
114  /*
115  * This method is only called while links are NOT "moved", i.e. their
116  * doStimStep(...) methods are called. To ensure that, we use a boolean lock.
117  * cdobler, sep'14
118  */
119  @Override
120  protected final void registerLinkAsActive(QLinkI link) {
121  if (!lockLinks) linksList.add(link);
122  else throw new RuntimeException("Tried to activate a QLink at a time where this was not allowed. Aborting!");
123  }
124 
125  @Override
126  public final int getNumberOfSimulatedLinks() {
127  return this.linksList.size();
128  }
129 
130  /*
131  * This method is only called while nodes are NOT "moved", i.e. their
132  * doStimStep(...) methods are called. To ensure that, we use a boolean lock.
133  * cdobler, sep'14
134  */
135  @Override
136  protected final void registerNodeAsActive(QNodeI node) {
137  if (!this.lockNodes) this.nodesQueue.add(node);
138  else throw new RuntimeException("Tried to activate a QNode at a time where this was not allowed. Aborting!");
139  }
140 
141  /*
142  * Note that the size() method is O(n) for a ConcurrentLinkedQueue as used
143  * for the nodesQueue. However, this method is only called once every simulated
144  * hour for the log message. Therefore, it should be okay.
145  * cdobler, sep'14
146  */
147  @Override
148  public final int getNumberOfSimulatedNodes() {
149  return this.nodesQueue.size();
150  }
151 
152  protected final void startMeasure() {
153  if (QSim.analyzeRunTimes) this.startTime = System.nanoTime();
154  }
155 
156  protected final void endMeasure() {
157  if (QSim.analyzeRunTimes) {
158  long end = System.nanoTime();
159  int bin = (int) this.time;
160  if (bin < this.runTimes.length) this.runTimes[bin] = end - this.startTime;
161  }
162  }
163 }