| > arguably on that basis you could claim that they aren't quite 'cooperative multitasking' on their own Right, I think this is where I am coming from. Generators, for example, can be implemented via coroutines, but I would not call a generator "cooperative multitasking." That's very cool! Yeah, I have never done this myself, but in my understanding implementations in assembly can be very small. > when i went to go look at that just now i was hoping to come up with some kind of crisp statement about the relative importance or complexity of the stack-switching functionality and the run-queue maintenance facility, but in fact there isn't a clear separation between them That's fair, but I don't think that's the final say here, as you were building a system for cooperative multitasking explicitly, with no reason to try and separate the concerns. When a system is very simple, there's much less reason for separation. Actually, this makes me realize why I probably have this bias for thinking of them separately: async/await in Rust. The syntax purely creates a generator, it is totally inert. You have to bring along your own executor (which contains a scheduler among other things). Separating the two cleanly was an explicit design goal. |
it certainly isn't the final say! it's just an analysis of how my own code turned out, not any kind of universal lesson
the implementation in monokokko, which reserves the r10 register to always point to the currently running task, is five instructions
interestingly, what you say of rust's generators is also sort of true of monokokko> The syntax purely creates a generator, it is totally inert. You have to bring along your own executor (which contains a scheduler among other things).
the above five instructions, or arguably just ldr r10, [r10], is the executor. the in-memory task object consists of the saved stack pointer, the link to the following task, and then whatever variables you have in thread-local storage. but from a different point of view you could say that the in-memory task object consists of the saved stack pointer, a pointer to executor-specific status information (which for this executor is the following task, or conceptually the linked list of all tasks), and then other thread-local variables. i think the difference if you were to implement this same executor with rust generators is just that you probably wouldn't make the linked list of all tasks an intrusive list?