- Where: zoom.us
- When: October 18th, 16:00-17:00 UTC ( October 18th, 9am-10am Pacific Standard Time)
- Location: Zoom call
- Francis McCabe
- Lars Hansen
- Ben Titzer
- Ayke van Laethem
- Ioanna Dimitriou
- Derek Schuff
- Arjun Guha
- Ross Tate
- Thibaud Michaud
- Sam Lindley
- Deepti Gandluri
- Heejin Ahn
- Andreas Rossberg
- Michael Knyszek
- Luke Wagner
- Paul Schoenfelder
- Opening, welcome and roll call
- Opening of the meeting
- Find volunteers for note taking (acting chair to volunteer)
- Adoption of the agenda
- Discussions
-
TinyGo(Ayke van Laethem)
-
Next meeting [2 mins].
-
- Closure
Presentation of tinygo by Ayke van Laethem (AL)
Ayke presenting the slides. TinyGo, a go compiler for small embedded devices. Garbage collection and green threads, relevant here. In Go every function can be potentially blocking, a problem with Wasm.
- Francis: Is this true for tiny go or go generally?
- Ayke: These two features are for Go in general, but the way tinyGo implements things are a little bit different than the standard compiler. E.g. tinyGo doesn't do parallelism yet, but it does concurrency, as it is a language feature.
- Francis: sounds good.
Continuing with presentation: Slide "Option 1: LLVM coroutines" Not very maintainable.
Slide "Option 2: Asyncify" Less convenient.
Slide "Option 3: native stack switching in Wasm?" Ayke's ideal API solution.
- Francis: About Asyncify, did you use asyncify to implement Go routines?
- Ayke: Someone else did this, and I believe it's working but I haven't looked at it yet.
- Francis: Wondering about the cost of using asyncify there.
- Ayke: It's still WIP, haven't compared the cost, it's interesting. I hope it's less than LLVM.
- Francis: Was option 1 costly in effort?
- Ayke: In maintainability. When trying to switch to LLVM12 the compiler got smarter, and it was hard to do this change, in practice it seems hard to keep up with the changes to LLVM and get them right.
- Francis: Any examples?
- Ayke: For example in Go there is the "go" keyword. For example in some occasions (?) the Go code goes to sleep and not returns. LLVM corrects this, so assumes the code after that is unreachable. Trying to go around this, could not find fixes.
- Francis: I can see how this is a problem.
Continuing with slide "Option 3", the API that would be exposed to the programmer: new_stack
, switch_stack
, destroy_stack
.
-
Sam: More detail on the API functions?
-
Ayke:
new_stack
will create a new stack with an opaque id, a number or whatever. Has a function and initial parameter, one should be enough, any more could go to the heap.switch_stack
would switch to the other stack and start running it. -
Sam: Switching stack to the same id twice would be an error?
-
Ayke: When you switch to your own stack?
-
Sam: Yes, would that be an error?
-
Ayke: Haven't thought about that, an error seems appropriate. Also not destroying the current stack.
-
Ben: Switching stack from A to B, then another stack switching to A would wait until A has returned from B?
-
Ayke: Do you mean from a parallelism point of view?
-
Ben: When I am suspended to B and another call switches back to me, does it wait until B is finished?
-
Francis: How would a call know when to switch to which stack?
-
Ayke: Runtime should take care of it.
-
Ross: How do you change to the originating stack?
-
Ayke: An idea would be to give it a fixed id, like 0 and not allow destroying that stack. Destroying stacks is the responsibility of a garbage collector.
-
Francis: What happens to the return value from a called thunk?
-
Ayke: I guess a void.
-
Francis: So return value is ignored?
-
Ayke: Yes, because when it returns it exits the coroutine, so returned values would be ignored.
-
Sam: Giving the originating stack 0 id, how does it work with multiple such stacks?
-
Ayke: 0 is the originating one.
-
Ross: You could have a way to return the current stack id.
-
Ayke: Yes, that could be useful.
-
Ben: ...
-
Ayke: ...
-
??: How often do you switch stacks?
-
Ayke: Depends on how often you do IO.
-
Ross: Presumably also when a channel read happens. How often is that?
-
Ayke: Depends, happens often, but less often than you'd call a function.
-
Ross: So having noticeable overhead in stack switching would have an effect.
-
Ayke: Yes, you wouldn't want this to be expensive.
-
Francis: TinyGo for embedded systems?
-
Ayke: yes.
-
Francis: So you're relying on an external event loop for IO, is this how it's done or are you doing it yourself?
-
Ayke: The thing with coroutines, this is unrelated to Wasm, I'm mainly talking about wasi and the web.
-
Francis: ...
-
Ayke: Microcontrollers can interrupt at any time, stack switching that happens there are operational threads, no IO loop.
-
Sam: Are there interrupts that can happen in WebAssembly?
-
Ayke: not aware of any. Maybe if JS can call into Wasm, but JS is single threaded.
-
Sam: But a computation in Wasm should block.
-
Ayke: TinyGo is only single threaded at the moment.
-
Sam: So you'd have to return to allow other threads to run?
-
Ayke: Spec is silent, haven't implemented it.
... ...
-
Ayke: Interrupts are preemptive, there you store the program counter, start executing somewhere else.
-
Sam: Not the case for Wasm
-
Ayke: right, so Wasm wouldn't be interrupted.
-
Ben: Do you check whether you have space to switch on other systems?
-
Ayke: There is a check when switching stacks, checking for overflow.
-
Ross: So buggy code leads to system crash.
-
Ayke: Yes, it's not perfect. Some microcontrollers would try to compute maximum stack space, but not possible when you have recursion.
-
Francis: What is more important for TinyGo? Wasm or microcontrollers?
-
Ayke: I'm more interested in microcontrollers but running Wasm is important to others.
-
Francis: Wasm on microcontrollers is also interesting.
-
Ayke: Wasm page size is small for a laptop but huge for microcontrollers.
-
Ross: Any more slides?
-
Ayke: This is all I have to show from what we'd like to have from Wasm. Reason for this talk is to share how TinyGo does this, and make sure it's considered.
-
Ross: How to determine who to switch to next?
-
Ayke: Runtime to determine this. Currently using a scheduler.
-
Ross: For example Erlang does the computation before switching.
-
Ayke: Could be either way.
-
Francis: Another way to express Ross's question: Sleep does a yield to the scheduler, so you'd have to know the id of the scheduler.
-
Ayke: That's an implementation detail, but there has to be a main stack, otherwise you can't run Wasm. Currently I'm using the main stack as the scheduler, and the interrupt stack, but that's another issue.
-
Francis: If you're running a runtime, what is the runtime written in? If it's done in Wasm, then you'd have to do the switching in Wasm.
-
Ayke: How I imagine it: entry function runs the scheduler which creates a new stack for the main coroutine (all written in Wasm). When you want to switch stacks you switch back to the scheduler. The entire code is run in go which is compiled to WebAssembly. So the Go runtime is generic Go code, compiled to WebAssembly, so in general is Wasm.
-
Francis: Back to microcontrollers, how many threads do you expect to see?
-
Ayke: Usually one or just a few due to memory limitations. Don't think people are running hundreds of them, but people might do that. For example a web server could be quite a few.
-
Francis: And these microcontrollers don't have real threads.
-
Ayke: So you mean parallelism?
-
Francis: Yes.
-
Ayke: There are microcontrollers with multiple cores, I am only aware of two, but the vast majority have only one core, very uncommon to have more.
-
Ross: If your scheduler is written in go, what happens if it does a send or receive?
-
Ayke: You shouldn't, there are limitations, like arbitrarily allocating memory. You shouldn't allocate memory while garbage collecting either.
-
Ross: So whoever is writing that code should be careful about what subset of the language you use.
-
Ayke: Yes.
-
Paul: Same happens in Erlang, when we're writing the scheduler. The scheduler thread is essentially privileged, we write it in Rust and compile that to Wasm.
-
Ayke: For us it's not privileged, but it's like in C, you should be more careful with memory.
-
Ross: We would need to know how the scheduler is compiled, whether it's privileged, to know of any constraints.
-
Ayke: In what way would it constrain the stack switching API if it's not privileged code?
-
Francis: Having the runtime in Go, even having the runtime in Rust, is putting constraints to Stacks spec.
-
Ayke: I could write some assembly directly then.
-
Paul: That's what we do in Erlang, some stack switching logic is written by hand.
-
Francis: When we say assembly we mean WebAssembly.
-
Ayke: That would be fine as well. Maybe it would inhibit a few optimisations but not a big deal.