| OLD | NEW |
| 1 // Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Fletch 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.md file. | 3 // BSD-style license that can be found in the LICENSE.md file. |
| 4 | 4 |
| 5 import 'dart:_fletch_system' as fletch; | 5 import 'dart:_fletch_system' as fletch; |
| 6 import 'dart:fletch'; | 6 import 'dart:fletch'; |
| 7 import 'dart:fletch.os' as os; | 7 import 'dart:fletch.os' as os; |
| 8 import 'dart:math'; | 8 import 'dart:math'; |
| 9 | 9 |
| 10 const patch = "patch"; | 10 const patch = "patch"; |
| 11 | 11 |
| 12 Channel _eventQueue; | 12 Channel _eventQueue; |
| 13 _FletchTimer _timers; | |
| 14 int _numberOfEvents = 0; | 13 int _numberOfEvents = 0; |
| 15 | 14 |
| 16 void _handleEvents() { | 15 void _handleEvents() { |
| 17 while (_numberOfEvents > 0) { | 16 while (_numberOfEvents > 0) { |
| 18 var event = _eventQueue.receive(); | 17 var event = _eventQueue.receive(); |
| 19 _numberOfEvents--; | 18 _numberOfEvents--; |
| 20 event(); | 19 event(); |
| 21 } | 20 } |
| 22 _eventQueue = null; | 21 _eventQueue = null; |
| 23 } | 22 } |
| 24 | 23 |
| 25 Channel _ensureEventQueue() { | 24 Channel _ensureEventQueue() { |
| 26 if (_eventQueue == null) { | 25 if (_eventQueue == null) { |
| 27 _eventQueue = new Channel(); | 26 _eventQueue = new Channel(); |
| 28 Fiber.fork(_handleEvents); | 27 Fiber.fork(_handleEvents); |
| 29 } | 28 } |
| 30 return _eventQueue; | 29 return _eventQueue; |
| 31 } | 30 } |
| 32 | 31 |
| 33 @patch class _AsyncRun { | 32 @patch class _AsyncRun { |
| 34 @patch static void _scheduleImmediate(void callback()) { | 33 @patch static void _scheduleImmediate(void callback()) { |
| 35 _numberOfEvents++; | 34 _numberOfEvents++; |
| 36 _ensureEventQueue().send(callback); | 35 _ensureEventQueue().send(callback); |
| 37 } | 36 } |
| 38 } | 37 } |
| 39 | 38 |
| 40 final int _maxWaitTimeInMilliseconds = 1000; | |
| 41 | |
| 42 final int _baseTime = new DateTime.now().millisecondsSinceEpoch; | |
| 43 | |
| 44 int get _currentTimestamp { | 39 int get _currentTimestamp { |
| 45 return new DateTime.now().millisecondsSinceEpoch - _baseTime; | 40 return new DateTime.now().millisecondsSinceEpoch; |
| 46 } | 41 } |
| 47 | 42 |
| 48 // TODO(ager): This is a pretty horrible implementation. We should get | 43 // TODO(ajohnsen): We should create a heap-like structure in Dart, so we only |
| 49 // this integrated with the event loop. For now this is enough to show | 44 // have one active port/channel per process. |
| 50 // that we can implement async on top of our current primitives. | |
| 51 class _FletchTimer implements Timer { | 45 class _FletchTimer implements Timer { |
| 52 final int _milliseconds; | 46 final int _milliseconds; |
| 53 var _callback; | 47 var _callback; |
| 54 int _timestamp = 0; | 48 int _timestamp = 0; |
| 55 _FletchTimer _next; | 49 _FletchTimer _next; |
| 56 bool _isActive = true; | 50 bool _isActive = true; |
| 57 bool _hasActiveWaitFiber = false; | 51 Channel _channel; |
| 52 Port _port; |
| 58 | 53 |
| 59 bool get _isPeriodic => _milliseconds >= 0; | 54 bool get _isPeriodic => _milliseconds >= 0; |
| 60 | 55 |
| 61 _FletchTimer(this._timestamp, this._callback) | 56 _FletchTimer(this._timestamp, this._callback) |
| 62 : _milliseconds = -1 { | 57 : _milliseconds = -1 { |
| 58 _channel = new Channel(); |
| 59 _port = new Port(_channel); |
| 63 _schedule(); | 60 _schedule(); |
| 64 _forkWaitFiber(); | |
| 65 } | 61 } |
| 66 | 62 |
| 67 _FletchTimer.periodic(this._timestamp, | 63 _FletchTimer.periodic(this._timestamp, |
| 68 void callback(Timer timer), | 64 void callback(Timer timer), |
| 69 this._milliseconds) { | 65 this._milliseconds) { |
| 70 _callback = () { callback(this); }; | 66 _callback = () { callback(this); }; |
| 67 _channel = new Channel(); |
| 68 _port = new Port(_channel); |
| 71 _schedule(); | 69 _schedule(); |
| 72 _forkWaitFiber(); | |
| 73 } | |
| 74 | |
| 75 void _forkWaitFiber() { | |
| 76 if (_timers != this) return; | |
| 77 assert(!_hasActiveWaitFiber); | |
| 78 _hasActiveWaitFiber = true; | |
| 79 Fiber.fork(() { | |
| 80 var milliseconds = _timestamp - _currentTimestamp; | |
| 81 if (milliseconds < 0) milliseconds = 0; | |
| 82 // Cap the sleep time so that a timer that is set way in the | |
| 83 // future will not cause the system to keep running if it is | |
| 84 // cancelled. | |
| 85 if (milliseconds > _maxWaitTimeInMilliseconds) { | |
| 86 milliseconds = _maxWaitTimeInMilliseconds; | |
| 87 } | |
| 88 | |
| 89 Channel channel = new Channel(); | |
| 90 Port port = new Port(channel); | |
| 91 Process.spawn((int milliseconds) { | |
| 92 os.sleep(milliseconds); | |
| 93 port.send(null); | |
| 94 }, milliseconds); | |
| 95 channel.receive(); | |
| 96 | |
| 97 _hasActiveWaitFiber = false; | |
| 98 _fireExpiredTimers(); | |
| 99 }); | |
| 100 } | |
| 101 | |
| 102 void _fireExpiredTimers() { | |
| 103 // Skip cancelled timers. | |
| 104 while (_timers != null && !_timers._isActive) _timers = _timers._next; | |
| 105 if (_timers != null) { | |
| 106 // If first timer is expired, fire it and reschedule if it is | |
| 107 // periodic. | |
| 108 if (_currentTimestamp >= _timers._timestamp) { | |
| 109 _AsyncRun._scheduleImmediate(_timers._callback); | |
| 110 var current = _timers; | |
| 111 _timers = _timers._next; | |
| 112 if (current._isPeriodic) current._reschedule(); | |
| 113 } | |
| 114 // Spin up a timer fiber if there isn't already one for | |
| 115 // the next timer to fire. | |
| 116 if (_timers != null && !_timers._hasActiveWaitFiber) { | |
| 117 _timers._forkWaitFiber(); | |
| 118 } | |
| 119 } | |
| 120 } | 70 } |
| 121 | 71 |
| 122 void _schedule() { | 72 void _schedule() { |
| 123 var lastWithSmallerTimestamp = null; | 73 Fiber.fork(() { |
| 124 for (_FletchTimer current = _timers; | 74 int value = _channel.receive(); |
| 125 current != null; | 75 if (value == 0 && _isActive) { |
| 126 current = current._next) { | 76 _callback(); |
| 127 if (current._timestamp > _timestamp) break; | 77 if (_isPeriodic) { |
| 128 lastWithSmallerTimestamp = current; | 78 _reschedule(); |
| 129 } | 79 } else { |
| 130 if (lastWithSmallerTimestamp == null) { | 80 _isActive = false; |
| 131 _next = _timers; | 81 } |
| 132 _timers = this; | 82 } |
| 133 } else { | 83 }); |
| 134 _next = lastWithSmallerTimestamp._next; | 84 _scheduleTimeout(_timestamp, _port); |
| 135 lastWithSmallerTimestamp._next = this; | |
| 136 } | |
| 137 } | 85 } |
| 138 | 86 |
| 139 void _reschedule() { | 87 void _reschedule() { |
| 140 assert(_isPeriodic); | 88 assert(_isPeriodic); |
| 141 _timestamp = _currentTimestamp + _milliseconds; | 89 _timestamp += _milliseconds; |
| 142 _schedule(); | 90 _schedule(); |
| 143 } | 91 } |
| 144 | 92 |
| 145 void cancel() { | 93 void cancel() { |
| 94 _scheduleTimeout(-1, _port); |
| 95 _port.send(-1); |
| 146 _isActive = false; | 96 _isActive = false; |
| 147 } | 97 } |
| 148 | 98 |
| 149 bool get isActive => _isActive; | 99 bool get isActive => _isActive; |
| 100 |
| 101 @fletch.native external static void _scheduleTimeout(int timeout, Port port); |
| 150 } | 102 } |
| 151 | 103 |
| 152 @patch class Timer { | 104 @patch class Timer { |
| 153 @patch static Timer _createTimer(Duration duration, void callback()) { | 105 @patch static Timer _createTimer(Duration duration, void callback()) { |
| 154 int milliseconds = max(0, duration.inMilliseconds); | 106 int milliseconds = max(0, duration.inMilliseconds); |
| 155 return new _FletchTimer(_currentTimestamp + milliseconds, callback); | 107 return new _FletchTimer(_currentTimestamp + milliseconds, callback); |
| 156 } | 108 } |
| 157 | 109 |
| 158 @patch static Timer _createPeriodicTimer(Duration duration, | 110 @patch static Timer _createPeriodicTimer(Duration duration, |
| 159 void callback(Timer timer)) { | 111 void callback(Timer timer)) { |
| 160 int milliseconds = max(0, duration.inMilliseconds); | 112 int milliseconds = max(0, duration.inMilliseconds); |
| 161 return new _FletchTimer.periodic(_currentTimestamp + milliseconds, | 113 return new _FletchTimer.periodic(_currentTimestamp + milliseconds, |
| 162 callback, | 114 callback, |
| 163 milliseconds); | 115 milliseconds); |
| 164 } | 116 } |
| 165 } | 117 } |
| OLD | NEW |