The quick answer is that coroutines are often used to implement cooperative multitasking because it is a very natural fit, but it's a more general idea than that specific implementation strategy.
interesting, i would have said the relationship is the other way around: cooperative multitasking implies that you have separate stacks that you're switching between, and coroutines are a more general idea which includes cooperative multitasking (as in lua) and things that aren't cooperative multitasking (as in rust and python) because the program's execution state isn't divided into distinct tasks
Yeah thinking about it more I didn’t intend to imply a subset relationship. Coroutines are not only used to implement cooperative multitasking, for sure.
well, i mean, lua's 'coroutines' are full tasks with their own stacks, unlike, say, python's 'coroutines'. so arguably it isn't that one can be used to implement the other; it's that they're two names for the same thing
lua's coroutines aren't automatically scheduled (there isn't a built-in run queue) but explicitly resumed, which is a difference from the usual cooperative-multitasking systems; arguably on that basis you could claim that they aren't quite 'cooperative multitasking' on their own
the last time i implemented a simple round-robin scheduler for cooperative multitasking was in july, as an exercise, and it was in arm assembly language rather than lua. it was 32 machine instructions and 64 lines of code (http://canonical.org/~kragen/sw/dev3/monokokko.S), plus 14 lines of example code to run in the threads. 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, and that version of the code creates all the tasks at assembly time instead of runtime. a more flexible version with start, spawn, yield, and exit calls, which respects the eabi so you can write your task code in c (http://canonical.org/~kragen/sw/dev3/einkornix.het seq.), is 53 lines of assembly and 34 machine instructions, but similarly has no real separation of the two concerns