OLD | NEW |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dartino 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 library dart.fletch; | 5 library dart.dartino; |
6 | 6 |
7 import 'dart:fletch._system' as fletch; | 7 import 'dart:dartino._system' as dartino; |
8 | 8 |
9 /// Fibers are lightweight co-operative multitask units of execution. They | 9 /// Fibers are lightweight co-operative multitask units of execution. They |
10 /// are scheduled on top of OS-level threads, but they are cheap to create | 10 /// are scheduled on top of OS-level threads, but they are cheap to create |
11 /// and block. | 11 /// and block. |
12 class Fiber { | 12 class Fiber { |
13 | 13 |
14 // We keep track of the top of the coroutine stack and | 14 // We keep track of the top of the coroutine stack and |
15 // the list of other fibers that are waiting for this | 15 // the list of other fibers that are waiting for this |
16 // fiber to exit. | 16 // fiber to exit. |
17 Coroutine _coroutine; | 17 Coroutine _coroutine; |
(...skipping 15 matching lines...) Expand all Loading... |
33 static Fiber _idleFibers; | 33 static Fiber _idleFibers; |
34 | 34 |
35 Fiber._initial() { | 35 Fiber._initial() { |
36 _previous = this; | 36 _previous = this; |
37 _next = this; | 37 _next = this; |
38 } | 38 } |
39 | 39 |
40 Fiber._forked(entry) { | 40 Fiber._forked(entry) { |
41 current; // Force initialization of fiber sub-system. | 41 current; // Force initialization of fiber sub-system. |
42 _coroutine = new Coroutine((ignore) { | 42 _coroutine = new Coroutine((ignore) { |
43 fletch.runToEnd(entry); | 43 dartino.runToEnd(entry); |
44 }); | 44 }); |
45 } | 45 } |
46 | 46 |
47 static Fiber get current { | 47 static Fiber get current { |
48 var current = _current; | 48 var current = _current; |
49 if (current != null) return current; | 49 if (current != null) return current; |
50 return _current = new Fiber._initial(); | 50 return _current = new Fiber._initial(); |
51 } | 51 } |
52 | 52 |
53 static Fiber fork(entry) { | 53 static Fiber fork(entry) { |
54 Fiber fiber = new Fiber._forked(entry); | 54 Fiber fiber = new Fiber._forked(entry); |
55 _markReady(fiber); | 55 _markReady(fiber); |
56 return fiber; | 56 return fiber; |
57 } | 57 } |
58 | 58 |
59 static void yield() { | 59 static void yield() { |
60 // Handle messages so that fibers that are blocked on receiving | 60 // Handle messages so that fibers that are blocked on receiving |
61 // messages can wake up. | 61 // messages can wake up. |
62 Process._handleMessages(); | 62 Process._handleMessages(); |
63 _current._coroutine = Coroutine._coroutineCurrent(); | 63 _current._coroutine = Coroutine._coroutineCurrent(); |
64 _schedule(_current._next); | 64 _schedule(_current._next); |
65 } | 65 } |
66 | 66 |
67 static void exit([value]) { | 67 static void exit([value]) { |
68 // If we never created a fiber, we can just go ahead and halt now. | 68 // If we never created a fiber, we can just go ahead and halt now. |
69 if (_current == null) fletch.halt(); | 69 if (_current == null) dartino.halt(); |
70 | 70 |
71 _current._exit(value); | 71 _current._exit(value); |
72 } | 72 } |
73 | 73 |
74 join() { | 74 join() { |
75 // If the fiber is already done, we just return the result. | 75 // If the fiber is already done, we just return the result. |
76 if (_isDone) return _result; | 76 if (_isDone) return _result; |
77 | 77 |
78 // Add the current fiber to the list of fibers waiting | 78 // Add the current fiber to the list of fibers waiting |
79 // to join [this] fiber. | 79 // to join [this] fiber. |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 if (exiting) { | 152 if (exiting) { |
153 fiber._next = null; | 153 fiber._next = null; |
154 fiber._previous = null; | 154 fiber._previous = null; |
155 } else { | 155 } else { |
156 _idleFibers = _link(fiber, _idleFibers); | 156 _idleFibers = _link(fiber, _idleFibers); |
157 } | 157 } |
158 | 158 |
159 if (current != null) return current; | 159 if (current != null) return current; |
160 | 160 |
161 // If we don't have any idle fibers, halt. | 161 // If we don't have any idle fibers, halt. |
162 if (exiting && _idleFibers == null) fletch.halt(); | 162 if (exiting && _idleFibers == null) dartino.halt(); |
163 | 163 |
164 while (true) { | 164 while (true) { |
165 Process._handleMessages(); | 165 Process._handleMessages(); |
166 // A call to _handleMessages can handle more messages than signaled, so | 166 // A call to _handleMessages can handle more messages than signaled, so |
167 // we can get a following false-positive wakeup. If no new _current is | 167 // we can get a following false-positive wakeup. If no new _current is |
168 // set, simply yield again. | 168 // set, simply yield again. |
169 current = _current; | 169 current = _current; |
170 if (current != null) return current; | 170 if (current != null) return current; |
171 fletch.yield(fletch.InterruptKind.yield.index); | 171 dartino.yield(dartino.InterruptKind.yield.index); |
172 } | 172 } |
173 } | 173 } |
174 | 174 |
175 static void _yieldTo(Fiber from, Fiber to) { | 175 static void _yieldTo(Fiber from, Fiber to) { |
176 from._coroutine = Coroutine._coroutineCurrent(); | 176 from._coroutine = Coroutine._coroutineCurrent(); |
177 _schedule(to); | 177 _schedule(to); |
178 } | 178 } |
179 | 179 |
180 // TODO(kasperl): This is temporary debugging support. We | 180 // TODO(kasperl): This is temporary debugging support. We |
181 // should probably replace this support for passing in an | 181 // should probably replace this support for passing in an |
182 // id of some sort when forking a fiber. | 182 // id of some sort when forking a fiber. |
183 static int _count = 0; | 183 static int _count = 0; |
184 int _index = _count++; | 184 int _index = _count++; |
185 toString() => "fiber:$_index"; | 185 toString() => "fiber:$_index"; |
186 | 186 |
187 static void _schedule(Fiber to) { | 187 static void _schedule(Fiber to) { |
188 _current = to; | 188 _current = to; |
189 fletch.coroutineChange(to._coroutine, null); | 189 dartino.coroutineChange(to._coroutine, null); |
190 } | 190 } |
191 } | 191 } |
192 | 192 |
193 class Coroutine { | 193 class Coroutine { |
194 | 194 |
195 // TODO(kasperl): The VM has assumptions about the layout | 195 // TODO(kasperl): The VM has assumptions about the layout |
196 // of coroutine fields. We should validate that the code | 196 // of coroutine fields. We should validate that the code |
197 // agrees with those assumptions. | 197 // agrees with those assumptions. |
198 var _stack; | 198 var _stack; |
199 var _caller; | 199 var _caller; |
200 | 200 |
201 Coroutine(entry) { | 201 Coroutine(entry) { |
202 _stack = _coroutineNewStack(this, entry); | 202 _stack = _coroutineNewStack(this, entry); |
203 } | 203 } |
204 | 204 |
205 bool get isSuspended => identical(_caller, null); | 205 bool get isSuspended => identical(_caller, null); |
206 bool get isRunning => !isSuspended && !isDone; | 206 bool get isRunning => !isSuspended && !isDone; |
207 bool get isDone => identical(_caller, this); | 207 bool get isDone => identical(_caller, this); |
208 | 208 |
209 call(argument) { | 209 call(argument) { |
210 if (!isSuspended) throw "Cannot call non-suspended coroutine"; | 210 if (!isSuspended) throw "Cannot call non-suspended coroutine"; |
211 _caller = _coroutineCurrent(); | 211 _caller = _coroutineCurrent(); |
212 var result = fletch.coroutineChange(this, argument); | 212 var result = dartino.coroutineChange(this, argument); |
213 | 213 |
214 // If the called coroutine is done now, we clear the | 214 // If the called coroutine is done now, we clear the |
215 // stack reference in it so the memory can be reclaimed. | 215 // stack reference in it so the memory can be reclaimed. |
216 if (isDone) { | 216 if (isDone) { |
217 _stack = null; | 217 _stack = null; |
218 } else { | 218 } else { |
219 _caller = null; | 219 _caller = null; |
220 } | 220 } |
221 return result; | 221 return result; |
222 } | 222 } |
223 | 223 |
224 static yield(value) { | 224 static yield(value) { |
225 Coroutine caller = _coroutineCurrent()._caller; | 225 Coroutine caller = _coroutineCurrent()._caller; |
226 if (caller == null) throw "Cannot yield outside coroutine"; | 226 if (caller == null) throw "Cannot yield outside coroutine"; |
227 return fletch.coroutineChange(caller, value); | 227 return dartino.coroutineChange(caller, value); |
228 } | 228 } |
229 | 229 |
230 _coroutineStart(entry) { | 230 _coroutineStart(entry) { |
231 // The first call to changeStack is actually skipped but we need | 231 // The first call to changeStack is actually skipped but we need |
232 // it to make sure the newly started coroutine is suspended in | 232 // it to make sure the newly started coroutine is suspended in |
233 // exactly the same way as we do when yielding. | 233 // exactly the same way as we do when yielding. |
234 var argument = fletch.coroutineChange(0, 0); | 234 var argument = dartino.coroutineChange(0, 0); |
235 var result = entry(argument); | 235 var result = entry(argument); |
236 | 236 |
237 // Mark this coroutine as done and change back to the caller. | 237 // Mark this coroutine as done and change back to the caller. |
238 Coroutine caller = _caller; | 238 Coroutine caller = _caller; |
239 _caller = this; | 239 _caller = this; |
240 fletch.coroutineChange(caller, result); | 240 dartino.coroutineChange(caller, result); |
241 } | 241 } |
242 | 242 |
243 @fletch.native external static _coroutineCurrent(); | 243 @dartino.native external static _coroutineCurrent(); |
244 @fletch.native external static _coroutineNewStack(coroutine, entry); | 244 @dartino.native external static _coroutineNewStack(coroutine, entry); |
245 } | 245 } |
246 | 246 |
247 class ProcessDeath { | 247 class ProcessDeath { |
248 final Process process; | 248 final Process process; |
249 final int _reason; | 249 final int _reason; |
250 | 250 |
251 ProcessDeath._(this.process, this._reason); | 251 ProcessDeath._(this.process, this._reason); |
252 | 252 |
253 DeathReason get reason => DeathReason.values[_reason]; | 253 DeathReason get reason => DeathReason.values[_reason]; |
254 } | 254 } |
(...skipping 12 matching lines...) Expand all Loading... |
267 final int _nativeProcessHandle; | 267 final int _nativeProcessHandle; |
268 | 268 |
269 bool operator==(other) { | 269 bool operator==(other) { |
270 return | 270 return |
271 other is Process && | 271 other is Process && |
272 other._nativeProcessHandle == _nativeProcessHandle; | 272 other._nativeProcessHandle == _nativeProcessHandle; |
273 } | 273 } |
274 | 274 |
275 int get hashCode => _nativeProcessHandle.hashCode; | 275 int get hashCode => _nativeProcessHandle.hashCode; |
276 | 276 |
277 @fletch.native bool link() { | 277 @dartino.native bool link() { |
278 throw fletch.nativeError; | 278 throw dartino.nativeError; |
279 } | 279 } |
280 | 280 |
281 @fletch.native void unlink() { | 281 @dartino.native void unlink() { |
282 switch (fletch.nativeError) { | 282 switch (dartino.nativeError) { |
283 case fletch.wrongArgumentType: | 283 case dartino.wrongArgumentType: |
284 throw new StateError("Cannot unlink from parent process."); | 284 throw new StateError("Cannot unlink from parent process."); |
285 default: | 285 default: |
286 throw fletch.nativeError; | 286 throw dartino.nativeError; |
287 } | 287 } |
288 } | 288 } |
289 | 289 |
290 @fletch.native bool monitor(Port port) { | 290 @dartino.native bool monitor(Port port) { |
291 switch (fletch.nativeError) { | 291 switch (dartino.nativeError) { |
292 case fletch.wrongArgumentType: | 292 case dartino.wrongArgumentType: |
293 throw new StateError("The argument to monitor must be a Port object."); | 293 throw new StateError("The argument to monitor must be a Port object."); |
294 default: | 294 default: |
295 throw fletch.nativeError; | 295 throw dartino.nativeError; |
296 } | 296 } |
297 } | 297 } |
298 | 298 |
299 @fletch.native void unmonitor(Port port) { | 299 @dartino.native void unmonitor(Port port) { |
300 switch (fletch.nativeError) { | 300 switch (dartino.nativeError) { |
301 case fletch.wrongArgumentType: | 301 case dartino.wrongArgumentType: |
302 throw new StateError( | 302 throw new StateError( |
303 "The argument to unmonitor must be a Port object."); | 303 "The argument to unmonitor must be a Port object."); |
304 default: | 304 default: |
305 throw fletch.nativeError; | 305 throw dartino.nativeError; |
306 } | 306 } |
307 } | 307 } |
308 | 308 |
309 @fletch.native void kill() { | 309 @dartino.native void kill() { |
310 throw fletch.nativeError; | 310 throw dartino.nativeError; |
311 } | 311 } |
312 | 312 |
313 static Process spawn(Function fn, [argument]) { | 313 static Process spawn(Function fn, [argument]) { |
314 if (!isImmutable(fn)) { | 314 if (!isImmutable(fn)) { |
315 throw new ArgumentError( | 315 throw new ArgumentError( |
316 'The closure passed to Process.spawn() must be immutable.'); | 316 'The closure passed to Process.spawn() must be immutable.'); |
317 } | 317 } |
318 | 318 |
319 if (!isImmutable(argument)) { | 319 if (!isImmutable(argument)) { |
320 throw new ArgumentError( | 320 throw new ArgumentError( |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
388 | 388 |
389 /** | 389 /** |
390 * Exit the current process. If a non-null [to] port is provided, | 390 * Exit the current process. If a non-null [to] port is provided, |
391 * the process will send the provided [value] to the [to] port as | 391 * the process will send the provided [value] to the [to] port as |
392 * its final action. | 392 * its final action. |
393 */ | 393 */ |
394 static void exit({value, Port to}) { | 394 static void exit({value, Port to}) { |
395 try { | 395 try { |
396 if (to != null) to._sendExit(value); | 396 if (to != null) to._sendExit(value); |
397 } finally { | 397 } finally { |
398 fletch.yield(fletch.InterruptKind.terminate.index); | 398 dartino.yield(dartino.InterruptKind.terminate.index); |
399 } | 399 } |
400 } | 400 } |
401 | 401 |
402 // Low-level entry for spawned processes. | 402 // Low-level entry for spawned processes. |
403 static void _entry(fn, argument) { | 403 static void _entry(fn, argument) { |
404 if (argument == null) { | 404 if (argument == null) { |
405 fletch.runToEnd(fn); | 405 dartino.runToEnd(fn); |
406 } else { | 406 } else { |
407 fletch.runToEnd(() => fn(argument)); | 407 dartino.runToEnd(() => fn(argument)); |
408 } | 408 } |
409 } | 409 } |
410 | 410 |
411 // Low-level helper function for spawning. | 411 // Low-level helper function for spawning. |
412 @fletch.native static Process _spawn(Function entry, | 412 @dartino.native static Process _spawn(Function entry, |
413 Function fn, | 413 Function fn, |
414 argument, | 414 argument, |
415 bool linkToChild, | 415 bool linkToChild, |
416 bool linkFromChild, | 416 bool linkFromChild, |
417 Port monitor) { | 417 Port monitor) { |
418 throw new ArgumentError(); | 418 throw new ArgumentError(); |
419 } | 419 } |
420 | 420 |
421 static void _handleMessages() { | 421 static void _handleMessages() { |
422 Channel channel; | 422 Channel channel; |
423 while ((channel = _queueGetChannel()) != null) { | 423 while ((channel = _queueGetChannel()) != null) { |
424 var message = _queueGetMessage(); | 424 var message = _queueGetMessage(); |
425 if (message is ProcessDeath) { | 425 if (message is ProcessDeath) { |
426 message = _queueSetupProcessDeath(message); | 426 message = _queueSetupProcessDeath(message); |
427 } | 427 } |
428 channel.send(message); | 428 channel.send(message); |
429 } | 429 } |
430 } | 430 } |
431 | 431 |
432 @fletch.native external static Process get current; | 432 @dartino.native external static Process get current; |
433 @fletch.native external static _queueGetMessage(); | 433 @dartino.native external static _queueGetMessage(); |
434 @fletch.native external static _queueSetupProcessDeath(ProcessDeath message); | 434 @dartino.native external static _queueSetupProcessDeath(ProcessDeath message); |
435 @fletch.native external static Channel _queueGetChannel(); | 435 @dartino.native external static Channel _queueGetChannel(); |
436 } | 436 } |
437 | 437 |
438 // Ports allow you to send messages to a channel. Ports are | 438 // Ports allow you to send messages to a channel. Ports are |
439 // are transferable and can be sent between processes. | 439 // are transferable and can be sent between processes. |
440 class Port { | 440 class Port { |
441 // A Smi stores the aligned pointer to the C++ port object. | 441 // A Smi stores the aligned pointer to the C++ port object. |
442 final int _port; | 442 final int _port; |
443 | 443 |
444 factory Port(Channel channel) { | 444 factory Port(Channel channel) { |
445 return Port._create(channel); | 445 return Port._create(channel); |
446 } | 446 } |
447 | 447 |
448 // TODO(kasperl): Temporary debugging aid. | 448 // TODO(kasperl): Temporary debugging aid. |
449 int get id => _port; | 449 int get id => _port; |
450 | 450 |
451 // Send a message to the channel. Not blocking. | 451 // Send a message to the channel. Not blocking. |
452 @fletch.native void send(message) { | 452 @dartino.native void send(message) { |
453 switch (fletch.nativeError) { | 453 switch (dartino.nativeError) { |
454 case fletch.wrongArgumentType: | 454 case dartino.wrongArgumentType: |
455 throw new ArgumentError(); | 455 throw new ArgumentError(); |
456 case fletch.illegalState: | 456 case dartino.illegalState: |
457 throw new StateError("Port is closed."); | 457 throw new StateError("Port is closed."); |
458 default: | 458 default: |
459 throw fletch.nativeError; | 459 throw dartino.nativeError; |
460 } | 460 } |
461 } | 461 } |
462 | 462 |
463 @fletch.native void _sendExit(value) { | 463 @dartino.native void _sendExit(value) { |
464 throw new StateError("Port is closed."); | 464 throw new StateError("Port is closed."); |
465 } | 465 } |
466 | 466 |
467 @fletch.native external static Port _create(Channel channel); | 467 @dartino.native external static Port _create(Channel channel); |
468 } | 468 } |
469 | 469 |
470 class Channel { | 470 class Channel { |
471 Fiber _receiver; // TODO(kasperl): Should this be a queue too? | 471 Fiber _receiver; // TODO(kasperl): Should this be a queue too? |
472 | 472 |
473 // TODO(kasperl): Maybe make this a bit smarter and store | 473 // TODO(kasperl): Maybe make this a bit smarter and store |
474 // the elements in a growable list? Consider allowing bounds | 474 // the elements in a growable list? Consider allowing bounds |
475 // on the queue size. | 475 // on the queue size. |
476 _ChannelEntry _head; | 476 _ChannelEntry _head; |
477 _ChannelEntry _tail; | 477 _ChannelEntry _tail; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 | 536 |
537 class _ChannelEntry { | 537 class _ChannelEntry { |
538 final message; | 538 final message; |
539 final Fiber sender; | 539 final Fiber sender; |
540 _ChannelEntry next; | 540 _ChannelEntry next; |
541 _ChannelEntry(this.message, this.sender); | 541 _ChannelEntry(this.message, this.sender); |
542 } | 542 } |
543 | 543 |
544 bool isImmutable(Object object) => _isImmutable(object); | 544 bool isImmutable(Object object) => _isImmutable(object); |
545 | 545 |
546 @fletch.native external bool _isImmutable(String string); | 546 @dartino.native external bool _isImmutable(String string); |
547 | 547 |
548 /// Returns a channel that will receive a message in [milliseconds] | 548 /// Returns a channel that will receive a message in [milliseconds] |
549 /// milliseconds. | 549 /// milliseconds. |
550 // TODO(sigurdm): Move this function? | 550 // TODO(sigurdm): Move this function? |
551 Channel sleep(int milliseconds) { | 551 Channel sleep(int milliseconds) { |
552 if (milliseconds is! int) throw new ArgumentError(milliseconds); | 552 if (milliseconds is! int) throw new ArgumentError(milliseconds); |
553 Channel channel = new Channel(); | 553 Channel channel = new Channel(); |
554 Port port = new Port(channel); | 554 Port port = new Port(channel); |
555 _sleep(milliseconds, port); | 555 _sleep(milliseconds, port); |
556 return channel; | 556 return channel; |
557 } | 557 } |
558 | 558 |
559 @fletch.native external void _sleep(int milliseconds, Port port); | 559 @dartino.native external void _sleep(int milliseconds, Port port); |
OLD | NEW |