Live data from Hacker News

Implementing simple cooperative threads in C

brennan.io

11–20 of 88 posts

Re: Implementing simple cooperative threads in C

#11

Interesting to compare and contrast with Simon Tatham's method of implementing Co-Routines in C: https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html Submitted here: https://news.ycombinator.com/item?id=23293835 That's decades old, and actually used in PuTTY.

And libco: https://byuu.net/design/cooperative-threading

Re: Implementing simple cooperative threads in C

#12
post #9

FWIW, the original implementation of Java (when it was known as Oak) used this trick to manage user level threading. There was also a some code to do the stack swap since you were in a virtual machine anyway, that stuff was all there to be adjusted. Random meta note brenns10, the idiom for giving up the processor is 'yield' (rather than 'relinquish') it is common in cooperative systems. That said, the code in Java th…

> FWIW, the original implementation of Java (when it was known as Oak) used this trick to manage user level threading. There was also a some code to do the stack swap since you were in a virtual machine anyway, that stuff was all there to be adjusted.

I never knew Java did this. I figured most "interpreted" languages (via direct interpreters or bytecode) would be trivial to implement any kind of threads (even pre-emptive), because at any point the interpreter could decide to save the state of one language thread and switch to another, without bothering the interpreter's own stack.

> the idiom for giving up the processor is 'yield' (rather than 'relinquish') it is common in cooperative systems

Makes sense, I had been using relinquish for my other projects but I almost named it yield this time around.

> And my last comment is that if you set up your scheduler in a way that it has a 'runnable' and 'sleeping' queue, you can do clever things like have a task sleep on a mutex or other conditional variable and wake up when that variable does what you're waiting for. It makes implementing network services that service multiple clients much cleaner.

I'm really excited to tweak large parts of this, scheduler included! I'm really interested in allowing my scheduler to use `poll()` to determine readiness of blocked tasks, and yeah unblocking tasks for purposes like coroutines or mutexes or condition variables. This was the absolute minimum scheduler which would form a foundation to write baout, and I was really excited to write about the topic and share about it.

Re: Implementing simple cooperative threads in C

#13
I implemented something similar last year: https://github.com/kccqzy/green-threads/blob/master/green_th...

Actually implementing it was quite fun and I encourage every enterprising systems engineer to try. My implementation even supports growable stacks (up to a limit of course).

Re: Implementing simple cooperative threads in C

#15
we used something similar when I was writing games in the early to mid 90's, but our yield function was simply called yield().

we were running on windows (3.11), macOS, and amiga (for tooling, the games didn't run on the amiga) with great success.

of course, we just used setjmp and longjmp, no assembly for portability (though there ended up being some assembly in one of the games, while macOS had good sound management, windows did not at the time and the audio mixing code ended up written in hand-rolled x86 assembly in order to perform well enough for the games to work on the hardware at the time).

Re: Implementing simple cooperative threads in C

#16
post #5

Earlier quoted context omitted.

Faster I can believe, but how can custom asm be more portable than setjmp/longjmp which are standard since C89? Although in this particular instance TFA also uses inline assembly to setup the stack, so it's moot.

I'm wrong, I seemed to remember many platforms not supporting them but a quick Google search reveals its just a foggy memory. I was probably thinking about ucontext actually.

Most if not all RTOS's swap threads by saving and restoring the stack and registers. Most of the ones I've seen you can turn off preemption and you then have cooperative threads.

It's much easier to deal with shared memory with cooperative threads at the expense of higher latency.

Re: Implementing simple cooperative threads in C

#17
post #9

FWIW, the original implementation of Java (when it was known as Oak) used this trick to manage user level threading. There was also a some code to do the stack swap since you were in a virtual machine anyway, that stuff was all there to be adjusted. Random meta note brenns10, the idiom for giving up the processor is 'yield' (rather than 'relinquish') it is common in cooperative systems. That said, the code in Java th…

Is this why java.lang.Thread still has the yield() method?

All Java SE implementations I've ever seen use (preemptive) OS threads, so I assumed it's been this way from the beginning, and this yield() never made much sense to me.

Re: Implementing simple cooperative threads in C

#18
post #9

FWIW, the original implementation of Java (when it was known as Oak) used this trick to manage user level threading. There was also a some code to do the stack swap since you were in a virtual machine anyway, that stuff was all there to be adjusted. Random meta note brenns10, the idiom for giving up the processor is 'yield' (rather than 'relinquish') it is common in cooperative systems. That said, the code in Java th…

> FWIW, the original implementation of Java (when it was known as Oak) used this trick to manage user level threading. There was also a some code to do the stack swap since you were in a virtual machine anyway, that stuff was all there to be adjusted. I never knew Java did this. I figured most "interpreted" languages (via direct interpreters or bytecode) would be trivial to implement any kind of threads (even pre-emp…

I think he was referring to this https://en.wikipedia.org/wiki/Green_threads

BTW mid-grey text on yellow makes your great article difficult to read. I've had to open it in eww to read it. Worth it though, thanks!

Re: Implementing simple cooperative threads in C

#19

we used something similar when I was writing games in the early to mid 90's, but our yield function was simply called yield(). we were running on windows (3.11), macOS, and amiga (for tooling, the games didn't run on the amiga) with great success. of course, we just used setjmp and longjmp, no assembly for portability (though there ended up being some assembly in one of the games, while macOS had good sound managemen…

Cool! I wrote one of these in the same timeframe to run coroutines on a dos extender in 32bit with flat addressing.

Re: Implementing simple cooperative threads in C

#20
post #4

> It’s like a goto statement, but it can even be used to jump outside of a function. It’s also a lot more difficult to read than a goto, since it looks like a regular function call. Ah! Blood pressure rising! > Like with goto, the common advice is to avoid Whew. And then goes on to make a really good case for complicated task perform with simple error handling. It’s a good application for not exactly general use coop…

Does your blood pressure also rise from use of exceptions? Because the pattern that emerges with setjmp/longjmp is pretty similar, and that's the most common use: to simulate what other languages offer with exceptions. The most prominent libraries I can think of that force you to do this are image related, libpng and libjpeg both use longjmp to handle errors [though IIRC it's optional in the latter].

Are there exceptions in C?

If I’m interviewing someone for a programming job and I see goto in there C code... they better have an amazing reason or they won’t be getting the job. Harsh, but it’s reality of how few people are suited for embedded programming.

Post reply on HN