forked from MightyPork/crsn
document coroutines
This commit is contained in:
@@ -286,7 +286,77 @@ It can also be written like this:
|
|||||||
...
|
...
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Coroutines
|
||||||
|
|
||||||
|
Croissant implements something that could be called "pre-emptive coroutines". They do not provide any performance gain,
|
||||||
|
but add asynchronicity to the program, and can work as generators!
|
||||||
|
|
||||||
|
There is no true parallelism, it is difficult to implement safely and efficiently with a global state.
|
||||||
|
|
||||||
|
### Spawning
|
||||||
|
|
||||||
|
*Any procedure can be used as a coroutine.*
|
||||||
|
|
||||||
|
A coroutine is created using the `spawn` instruction, which produces an object handle.
|
||||||
|
|
||||||
|
```
|
||||||
|
(spawn r0 do_stuff 1 2 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
At this point, the program is evenly divided between the original and the coroutine "thread".
|
||||||
|
|
||||||
|
The spawned coroutine is scheduled to run immediately after being spawned.
|
||||||
|
|
||||||
|
### Task switching
|
||||||
|
|
||||||
|
Coroutines take turns to execute the program. The scheduling interval can be configured.
|
||||||
|
|
||||||
|
Control can be given up using the `yield` instruction; for example, when waiting for a mutex. This happens automatically when
|
||||||
|
a `sleep` instruction is invoked.
|
||||||
|
|
||||||
|
### Race conditions
|
||||||
|
|
||||||
|
Take care when working with objects, resources and global registers: you can get race conditions
|
||||||
|
with coroutines. Use atomic instructions (`cas`, `casXX`, `bfcas`…) to implement synchronization.
|
||||||
|
|
||||||
|
The `casXX` instruction is very powerful: you can use one bit of a register as a mutex and the rest of it to store some useful data.
|
||||||
|
You can also use one register for up to 64 mutexes.
|
||||||
|
|
||||||
|
Remember to only use global registers (or buffer items) as mutexes: `g0`-`g15`. Each coroutine has its own set of *regular* registers.
|
||||||
|
|
||||||
|
### Using coroutines as generators
|
||||||
|
|
||||||
|
A coroutine can "yield a value" by invoking the `yield` instruction with an operand. This can be done any number of times.
|
||||||
|
|
||||||
|
```
|
||||||
|
(yield r0)
|
||||||
|
```
|
||||||
|
|
||||||
|
The coroutine is blocked until the value is consumed by someone. To consume a yielded value, read the coroutine object handle:
|
||||||
|
|
||||||
|
```
|
||||||
|
(spawn r5 foo)
|
||||||
|
|
||||||
|
(ld r0 @r5) ; read a yielded value
|
||||||
|
```
|
||||||
|
|
||||||
|
### Joining a coroutine
|
||||||
|
|
||||||
|
Use the `join` instruction with a coroutine object handle to wait for its completion.
|
||||||
|
|
||||||
|
A coroutine completes by calling `ret` at its top level. This naturally means that a coroutine can return values!
|
||||||
|
|
||||||
|
The returned values are placed in the result registers, just like with the `call` instruction.
|
||||||
|
|
||||||
|
```
|
||||||
|
(spawn r5 foo)
|
||||||
|
; ...
|
||||||
|
|
||||||
|
(join @r0)
|
||||||
|
; res0-res15 now contain return values
|
||||||
|
```
|
||||||
|
|
||||||
# Instruction Set
|
# Instruction Set
|
||||||
|
|
||||||
Crsn instruction set is composed of extensions.
|
Crsn instruction set is composed of extensions.
|
||||||
|
|||||||
Reference in New Issue
Block a user