Live data from Hacker News

Implementing simple cooperative threads in C

brennan.io

21–30 of 88 posts

Re: Implementing simple cooperative threads in C

#21

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.

That's really cool, and much lighter weight for the purpose of coroutines!

Still looking for something appropriately handle blocking and resuming without a full RTOS, but I do like both these options.

Re: Implementing simple cooperative threads in C

#22
post #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.

Giving a chance to threads/processes with the same priority to be scheduled without doing any I/O or syscall? Used this once.

Re: Implementing simple cooperative threads in C

#23
post #4

Earlier quoted context omitted.

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.

Not sure what presence or absence of the goto has to do with embedded programming.

Re: Implementing simple cooperative threads in C

#25

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.

Note that SGT's coroutines are stackless, whereas this article's coroutines have their own stack.

It's also worth noting that you don't need asm to change the stack pointer, if you don't mind being imprecise about its exact value by the size of a function invocation frame or so: you can use alloca() or variable length arrays to do arithmetic on the stack pointer! https://fanf.livejournal.com/105413.html

Re: Implementing simple cooperative threads in C

#26
post #4

Earlier quoted context omitted.

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.

I spend a lot of time dicking with embedded C code. I think using goto is reasonable some cases. The classic case is to short circuit a block of code when there is an error of some sort.

   function(){
   
   // early returns here.

   // stuff
   // more stuff

   if(oops_err){
     goto some_error;
   }

   // yet more stuff

   // normal exit
   return 0;
   
   // bad exit
   some_error:

   // clean up

   return oops_err;
   }
The above is fine, you avoid a convoluted execution path. Or cleanup and a return in the middle of a function. Looking at the top of the function you see early returns. Looking at the end you see the normal and error exits.

Re: Implementing simple cooperative threads in C

#28
I recall being taught a (fairly rudimentary?) explanation of the memory model that the available space is used from one side as the stack, and from the other side as the heap. Reading the article, I realised that memory management in a concurrent context is way more complex than that, but you don't go into a lot of details here (fair enough, that's not the main focus).

So each task gets 16k of stack space, but what if it runs out of it? These are allocated from the "main" heap, so they won't exactly run into each other, but might trigger UB or crash the program. This is something that a real scheduler (like the one in the OS's kernel) would have to deal with anyway, so how does, say, Linux, solve this? Giving a lot of stack space to each running thread would lead to fragmented and underused memory, or is it not a concern at the stack spaces you'd normally deal with?

Re: Implementing simple cooperative threads in C

#29
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…

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

Don't tell the Python programmers...

Re: Implementing simple cooperative threads in C

#30
post #28

I recall being taught a (fairly rudimentary?) explanation of the memory model that the available space is used from one side as the stack, and from the other side as the heap. Reading the article, I realised that memory management in a concurrent context is way more complex than that, but you don't go into a lot of details here (fair enough, that's not the main focus). So each task gets 16k of stack space, but what i…

> Giving a lot of stack space to each running thread would lead to fragmented and underused memory, or is it not a concern at the stack spaces you'd normally deal with?

That's exactly what happens, and it's not a problem in practice -- each thread gets 8 megabytes of memory allocated. (Note, due to demand paging, physical usage grows in 4kb increments.)

Post reply on HN