Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: sdk/lib/io/timer_impl.dart

Issue 99203010: Alternative Timer implementation using simply list-based priority heap. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.io; 5 part of dart.io;
6 6
Søren Gjesse 2013/12/12 08:11:52 Please provide a short description of the data str
Anders Johnsen 2013/12/13 13:47:54 Done.
7 class _Timer extends LinkedListEntry<_Timer> implements Timer { 7 class _TimerHeap {
8 List<_Timer> _list;
9 int _used = 0;
10
11 _TimerHeap([int initSize = 32])
Lasse Reichstein Nielsen 2013/12/12 09:57:19 How about starting with 31 (or any other power-of-
Anders Johnsen 2013/12/13 13:47:54 Done.
12 : _list = new List<_Timer>(initSize);
13
14 bool get isEmpty => _used == 0;
15 bool get isNotEmpty => _used > 0;
16
17 _Timer get first => _list[0];
18
19 bool isFirst(_Timer timer) => timer._index == 0;
20
21 void add(_Timer timer) {
22 if (_used == _list.length) {
23 _resize();
24 }
25 timer._index = _used++;
26 _list[timer._index] = timer;
27 _bubbleUp(timer);
28 }
29
30 _Timer removeFirst() {
31 var f = first;
32 remove(f);
33 return f;
34 }
35
36 void remove(_Timer timer) {
37 _used--;
38 if (isEmpty) return;
39 var last = _list[_used];
Søren Gjesse 2013/12/12 08:11:52 Maybe set _list[_used] to null even though it is n
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Absolutely. We don't want to keep old timers alive
Anders Johnsen 2013/12/13 13:47:54 Done.
Anders Johnsen 2013/12/13 13:47:54 Done.
40 _swap(timer, last);
Lasse Reichstein Nielsen 2013/12/12 09:57:19 I'd prefer keeping 'last' in a variable until we h
Anders Johnsen 2013/12/13 13:47:54 Done.
41 _bubbleUp(last);
Lasse Reichstein Nielsen 2013/12/12 09:57:19 If it actually bubbles anywhere by doing bubbleUp,
Anders Johnsen 2013/12/13 13:47:54 Done.
42 _bubbleDown(last);
43 }
44
45 void _resize() {
46 var newList = new List(_list.length * 2);
47 for (int i = 0; i < _used; i++) {
48 newList[i] = _list[i];
49 }
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Consider using newList.setRange(0, _used, _list).
Anders Johnsen 2013/12/13 13:47:54 Done.
50 _list = newList;
51 }
52
53 void _bubbleUp(_Timer timer) {
54 while (!isFirst(timer)) {
55 Timer parent = _parent(timer);
56 if (timer._compareTo(parent) < 0) {
57 _swap(timer, parent);
58 } else {
59 break;
60 }
61 }
62 }
63
64 void _bubbleDown(_Timer timer) {
65 while (true) {
66 int leftIndex = _leftChildIndex(timer._index);
67 int rightIndex = _rightChildIndex(timer._index);
68 _Timer newest = timer;
69 if (leftIndex < _used && _list[leftIndex]._compareTo(newest) < 0) {
70 newest = _list[leftIndex];
71 }
72 if (rightIndex < _used && _list[rightIndex]._compareTo(newest) < 0) {
73 newest = _list[rightIndex];
74 }
75 if (newest == timer) {
76 // We are where we should be, break.
77 break;
78 }
79 _swap(newest, timer);
80 }
81 }
82
83 void _swap(_Timer first, _Timer second) {
84 int tmp = first._index;
85 first._index = second._index;
86 second._index = tmp;
87 _list[first._index] = first;
88 _list[second._index] = second;
89 }
90
91 String toString() {
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Remove this. It's for debugging purposes only, I a
Anders Johnsen 2013/12/13 13:47:54 Done.
92 return "($_used, ${_list.sublist(0, _used)})";
93 }
94
95 Timer _parent(_Timer timer) => _list[_parentIndex(timer._index)];
96 Timer _leftChild(_Timer timer) => _list[_leftChildIndex(timer._index)];
97 Timer _rightChild(_Timer timer) => _list[_rightChildIndex(timer._index)];
98
99 static int _parentIndex(int index) => (index - 1) ~/ 2;
100 static int _leftChildIndex(int index) => 2 * index + 1;
101 static int _rightChildIndex(int index) => 2 * index + 2;
102 }
103
104 class _Timer implements Timer {
8 // Disables the timer. 105 // Disables the timer.
9 static const int _NO_TIMER = -1; 106 static const int _NO_TIMER = -1;
10 107
11 // Timers are ordered by wakeup time. 108 // Timers are ordered by wakeup time.
12 static LinkedList<_Timer> _timers = new LinkedList<_Timer>(); 109 static _TimerHeap _heap = new _TimerHeap();
110 static _Timer _firstZeroTimer;
111 static _Timer _lastZeroTimer;
112 static int _idCount = 0;
13 113
14 static RawReceivePort _receivePort; 114 static RawReceivePort _receivePort;
15 static bool _handling_callbacks = false; 115 static bool _handling_callbacks = false;
Lasse Reichstein Nielsen 2013/12/12 09:57:19 -> _handlingCallbacks
Anders Johnsen 2013/12/13 13:47:54 Done.
16 116
17 Function _callback; 117 Function _callback;
18 int _milliSeconds; 118 int _milliSeconds;
19 int _wakeupTime = 0; 119 int _wakeupTime = 0;
120 _Timer _next;
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Consider letting this field and _index use the sam
Anders Johnsen 2013/12/13 13:47:54 Done.
121 int _index = -1;
122 int _id = -1;
123
124 String toString() => "($_wakeupTime, $_index, $_id)";
20 125
21 static Timer _createTimer(void callback(Timer timer), 126 static Timer _createTimer(void callback(Timer timer),
22 int milliSeconds, 127 int milliSeconds,
23 bool repeating) { 128 bool repeating) {
24 _Timer timer = new _Timer._internal(); 129 _Timer timer = new _Timer._internal();
25 timer._callback = callback; 130 timer._callback = callback;
26 if (milliSeconds > 0) { 131 if (milliSeconds > 0) {
27 // Add one because DateTime.now() is assumed to round down 132 // Add one because DateTime.now() is assumed to round down
28 // to nearest millisecond, not up, so that time + duration is before 133 // to nearest millisecond, not up, so that time + duration is before
29 // duration milliseconds from now. Using micosecond timers like 134 // duration milliseconds from now. Using micosecond timers like
30 // Stopwatch allows detecting that the timer fires early. 135 // Stopwatch allows detecting that the timer fires early.
31 timer._wakeupTime = 136 timer._wakeupTime =
32 new DateTime.now().millisecondsSinceEpoch + 1 + milliSeconds; 137 new DateTime.now().millisecondsSinceEpoch + 1 + milliSeconds;
33 } 138 }
34 timer._milliSeconds = repeating ? milliSeconds : -1; 139 timer._milliSeconds = repeating ? milliSeconds : -1;
35 timer._addTimerToList(); 140 if (timer._addTimerToHeap()) {
36 if (identical(timer, _timers.first)) {
37 // The new timer is the first in queue. Update event handler. 141 // The new timer is the first in queue. Update event handler.
38 timer._notifyEventHandler(); 142 timer._notifyEventHandler();
39 } 143 }
40 return timer; 144 return timer;
41 } 145 }
42 146
43 factory _Timer(int milliSeconds, void callback(Timer timer)) { 147 factory _Timer(int milliSeconds, void callback(Timer timer)) {
44 return _createTimer(callback, milliSeconds, false); 148 return _createTimer(callback, milliSeconds, false);
45 } 149 }
46 150
47 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) { 151 factory _Timer.periodic(int milliSeconds, void callback(Timer timer)) {
48 return _createTimer(callback, milliSeconds, true); 152 return _createTimer(callback, milliSeconds, true);
49 } 153 }
50 154
51 _Timer._internal() {} 155 _Timer._internal() {}
52 156
53 void _clear() { 157 void _clear() {
54 _callback = null; 158 _callback = null;
55 _milliSeconds = 0; 159 _milliSeconds = 0;
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Why clear milliSeconds? Is the number used for an
Anders Johnsen 2013/12/13 13:47:54 Done.
56 _wakeupTime = 0; 160 }
161
162 int _compareTo(_Timer other) {
163 int c = _wakeupTime - other._wakeupTime;
164 if (c != 0) return c;
165 return _id - other._id;
57 } 166 }
58 167
59 bool get _repeating => _milliSeconds >= 0; 168 bool get _repeating => _milliSeconds >= 0;
60 169
61 bool get isActive => _callback != null; 170 bool get isActive => _callback != null;
62 171
63 // Cancels a set timer. The timer is removed from the timer list and if 172 // Cancels a set timer. The timer is removed from the timer list and if
64 // the given timer is the earliest timer the native timer is reset. 173 // the given timer is the earliest timer the native timer is reset.
65 void cancel() { 174 void cancel() {
175 if (_index == -1) {
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Add comment: // Is in the zero-timer queue. Or add
Anders Johnsen 2013/12/13 13:47:54 Exactly :)
176 _clear();
177 return;
178 }
66 _clear(); 179 _clear();
Lasse Reichstein Nielsen 2013/12/12 09:57:19 You clear in both cases, so move the _clear() befo
Anders Johnsen 2013/12/13 13:47:54 Done.
67 // Return if already canceled. 180 // Return if already canceled.
68 if (list == null) return; 181 bool update = _firstZeroTimer == null;
69 assert(!_timers.isEmpty); 182 if (_wakeupTime != 0 && _index != -1) {
Lasse Reichstein Nielsen 2013/12/12 09:57:19 How can _index be -1 here? That case is special-ca
Anders Johnsen 2013/12/13 13:47:54 No, this was due to several iterations. I've clean
70 _Timer first = _timers.first; 183 update = update && _heap.isFirst(this);
71 unlink(); 184 _heap.remove(this);
72 if (identical(first, this)) { 185 _index = -1;
186 }
187 if (update) {
73 _notifyEventHandler(); 188 _notifyEventHandler();
74 } 189 }
75 } 190 }
76 191
77 void _advanceWakeupTime() { 192 void _advanceWakeupTime() {
78 assert(_milliSeconds >= 0); 193 assert(_milliSeconds >= 0);
79 _wakeupTime += _milliSeconds; 194 _wakeupTime += _milliSeconds;
80 } 195 }
81 196
82 // Adds a timer to the timer list. Timers with the same wakeup time are 197 // Adds a timer to the timer list. Timers with the same wakeup time are
83 // enqueued in order and notified in FIFO order. 198 // enqueued in order and notified in FIFO order.
84 void _addTimerToList() { 199 bool _addTimerToHeap() {
85 _Timer entry = _timers.isEmpty ? null : _timers.first; 200 if (_wakeupTime == 0) {
Lasse Reichstein Nielsen 2013/12/12 09:57:19 I would *love* if any timers in the heap that are
Anders Johnsen 2013/12/13 13:47:54 As discussed offline, will look into this later on
86 // If timer is last, add to end. 201 if (_firstZeroTimer == null) {
87 if (entry == null || _timers.last._wakeupTime <= _wakeupTime) { 202 _lastZeroTimer = _firstZeroTimer = this;
88 _timers.add(this); 203 return true;
89 return; 204 } else {
205 _lastZeroTimer = _lastZeroTimer._next = this;
206 return false;
207 }
208 } else {
209 _id = _idCount++;
210 _heap.add(this);
211 return _firstZeroTimer == null && _heap.isFirst(this);
90 } 212 }
91 // Otherwise scan through and find the right position.
92 while (entry != null) {
93 if (_wakeupTime < entry._wakeupTime) {
94 entry.insertBefore(this);
95 return;
96 }
97 entry = entry.next;
98 }
99 _timers.add(this);
100 } 213 }
101 214
102 215
103 void _notifyEventHandler() { 216 void _notifyEventHandler() {
104 if (_handling_callbacks) { 217 if (_handling_callbacks) {
105 // While we are already handling callbacks we will not notify the event 218 // While we are already handling callbacks we will not notify the event
106 // handler. _handleTimeout will call _notifyEventHandler once all pending 219 // handler. _handleTimeout will call _notifyEventHandler once all pending
107 // timers are processed. 220 // timers are processed.
108 return; 221 return;
109 } 222 }
110 223
111 if (_timers.isEmpty) { 224 if (_firstZeroTimer == null && _heap.isEmpty) {
112 // No pending timers: Close the receive port and let the event handler 225 // No pending timers: Close the receive port and let the event handler
113 // know. 226 // know.
114 if (_receivePort != null) { 227 if (_receivePort != null) {
115 _EventHandler._sendData(null, _receivePort, _NO_TIMER); 228 _EventHandler._sendData(null, _receivePort, _NO_TIMER);
116 _shutdownTimerHandler(); 229 _shutdownTimerHandler();
117 } 230 }
118 } else { 231 } else {
119 if (_receivePort == null) { 232 if (_receivePort == null) {
120 // Create a receive port and register a message handler for the timer 233 // Create a receive port and register a message handler for the timer
121 // events. 234 // events.
122 _createTimerHandler(); 235 _createTimerHandler();
123 } 236 }
124 _EventHandler._sendData(null, 237 _EventHandler._sendData(null,
125 _receivePort, 238 _receivePort,
126 _timers.first._wakeupTime); 239 _firstZeroTimer != null ?
240 0 : _heap.first._wakeupTime);
127 } 241 }
128 } 242 }
129 243
130 244
131 // Creates a receive port and registers the timer handler on that 245 // Creates a receive port and registers the timer handler on that
132 // receive port. 246 // receive port.
133 void _createTimerHandler() { 247 void _createTimerHandler() {
134 248
135 void _handleTimeout() { 249 void _handleTimeout() {
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Consider making _handleTimeout an instance method
Anders Johnsen 2013/12/13 13:47:54 Done.
136 int currentTime = new DateTime.now().millisecondsSinceEpoch; 250 int currentTime = new DateTime.now().millisecondsSinceEpoch;
137 251
138 // Collect all pending timers. 252 // Collect all pending timers.
139 var pending_timers = new List(); 253 var timer = _firstZeroTimer;
140 while (!_timers.isEmpty) { 254 var nextTimer = _lastZeroTimer;
141 _Timer entry = _timers.first; 255 _firstZeroTimer = _lastZeroTimer = null;
142 if (entry._wakeupTime <= currentTime) { 256 while (_heap.isNotEmpty && _heap.first._wakeupTime <= currentTime) {
143 entry.unlink(); 257 var next = _heap.removeFirst();
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Consider making removeFirst set the index to -1. T
Anders Johnsen 2013/12/13 13:47:54 Done.
144 pending_timers.add(entry); 258 next._index = -1;
259 if (timer == null) {
260 nextTimer = timer = next;
145 } else { 261 } else {
146 break; 262 nextTimer = nextTimer._next = next;
147 } 263 }
148 } 264 }
149 265
150 // Trigger all of the pending timers. New timers added as part of the 266 // Trigger all of the pending timers. New timers added as part of the
151 // callbacks will be enqueued now and notified in the next spin at the 267 // callbacks will be enqueued now and notified in the next spin at the
152 // earliest. 268 // earliest.
153 _handling_callbacks = true; 269 _handling_callbacks = true;
154 try { 270 try {
155 for (var timer in pending_timers) { 271 while (timer != null) {
272 var next = timer._next;
273 timer._next = null;
156 // One of the timers in the pending_timers list can cancel 274 // One of the timers in the pending_timers list can cancel
157 // one of the later timers which will set the callback to 275 // one of the later timers which will set the callback to
158 // null. 276 // null.
159 if (timer._callback != null) { 277 if (timer._callback != null) {
160 var callback = timer._callback; 278 var callback = timer._callback;
161 if (!timer._repeating) { 279 if (!timer._repeating) {
162 //Mark timer as inactive. 280 //Mark timer as inactive.
Lasse Reichstein Nielsen 2013/12/12 09:57:19 space between "//" and "M" :)
Anders Johnsen 2013/12/13 13:47:54 Done.
163 timer._callback = null; 281 timer._callback = null;
164 } 282 }
165 callback(timer); 283 callback(timer);
166 // Re-insert repeating timer if not canceled. 284 // Re-insert repeating timer if not canceled.
167 if (timer._repeating && timer._callback != null) { 285 if (timer._repeating && timer._callback != null) {
168 timer._advanceWakeupTime(); 286 timer._advanceWakeupTime();
169 timer._addTimerToList(); 287 timer._addTimerToHeap();
170 } 288 }
171 } 289 }
290 timer = next;
172 } 291 }
173 } finally { 292 } finally {
174 _handling_callbacks = false; 293 _handling_callbacks = false;
175 _notifyEventHandler(); 294 _notifyEventHandler();
176 } 295 }
177 } 296 }
178 297
179 if(_receivePort == null) { 298 if(_receivePort == null) {
180 _receivePort = new RawReceivePort((_) { _handleTimeout(); }); 299 _receivePort = new RawReceivePort((_) { _handleTimeout(); });
Lasse Reichstein Nielsen 2013/12/12 09:57:19 Avoid a wrapper closure by giving _handleTimeout t
Anders Johnsen 2013/12/13 13:47:54 Done.
181 } 300 }
182 } 301 }
183 302
184 void _shutdownTimerHandler() { 303 void _shutdownTimerHandler() {
185 _receivePort.close(); 304 _receivePort.close();
186 _receivePort = null; 305 _receivePort = null;
187 } 306 }
188 } 307 }
189 308
190 // Provide a closure which will allocate a Timer object to be able to hook 309 // Provide a closure which will allocate a Timer object to be able to hook
191 // up the Timer interface in dart:isolate with the implementation here. 310 // up the Timer interface in dart:isolate with the implementation here.
192 _getTimerFactoryClosure() { 311 _getTimerFactoryClosure() {
193 return (int milliSeconds, void callback(Timer timer), bool repeating) { 312 return (int milliSeconds, void callback(Timer timer), bool repeating) {
194 if (repeating) { 313 if (repeating) {
195 return new _Timer.periodic(milliSeconds, callback); 314 return new _Timer.periodic(milliSeconds, callback);
196 } 315 }
197 return new _Timer(milliSeconds, callback); 316 return new _Timer(milliSeconds, callback);
198 }; 317 };
199 } 318 }
200 319
201 320
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698