Hacker News new | past | comments | ask | show | jobs | submit login

Note that Simon's coroutines are more like Python generators than full-blown coroutines, since only the top-level function can yield.

You don't need asm to manipulate the stack and frame pointers in standard C - see http://fanf.livejournal.com/105413.html or for some slightly more fleshed-out variants that do not require C99 see http://dotat.at/cgi/git/picoro.git




So you allocate a coroutine stack by allocating a large array on the caller's stack, so that when you longjmp back to the setjmp'd context, you can safely overwrite that reserved space. F* me plenty.

Good stuff. One reason my inline asm serves my specific needs better is that my host OS has stack protection - a thread can only access its own stack; and I send coroutines around threads - yield in thread A, resume in thread B - and so I have to allocate my stacks outside the stacks of either A or B. I'll add a link to your implementation in my article, and perhaps Wikipedia should, too, if it didn't already (it has a lengthy list of coroutine implementations in C...)


Glad you mentioned this; to me, this is a very important requirement.

I don't see lot of people understand the difference between YIELD and full-blown coroutines.

IMO YEILD is not very useful; it doesn't compose well with non-yield function. OTOH full-blown coroutines are super awesome.

Too bad C++11 still doesn't give full-blown coroutines.


Could you explain what you mean by full-blown coroutines? Thanks!


If you have a function that can suspend its own execution and return data, you have a Python generator.

If it can also receive data when it is resumed, you have an ES6 generator (or the C macros above!).

If its subroutines can cause it to suspend, you have a full-blown coroutine.

If it can be copied, you can build call/cc.


I had know idea call/cc was that powerful. Very cool, and thanks for the breakdown for someone who did not know the difference.



Oops! "Python generator" should be "Python 2.4 generator." Versions of Python people actually use go with ES6.


Didn't see your comment til now. So, here is the difference I see:

Full-blown coroutines give us a way to write fully asynchronous code without using callbacks. For example, I could define ASYNC macro as follows:

#define ASYNC(blocking_function_call) \ do { \ auto io_pool = GetIOThreadPool(); \ auto cpu_pool = GetCurrentThreadPool(); \ \ Detach_The_Thread_and_Enqueue_Coroutine_For_IO(io_pool); \ \ blocking_function_call; \ \ Detach_The_Thread_and_Enqueue_Coroutine_For_CPU(cpu_pool); \ } while (0)

Now, I can make any blocking operation asyncrhonous. For example, open() or readdir() functions do not have asynchronous interfaces, but I can make them asynchronous by simply wrap it with ASYNC(...) macro.

It works as follows: When we need to do a blocking operation, we suspend the coroutine and we enqueue it into a thread-pool that is designated to do blocking calls; the cpu thread is now free, so it can start executing other cpu-bound coroutines. When io thread has finished the job, it will enqueue the coroutine back to the CPU thread pool.

What we have now is a fully asynchronous system where cpu threads can be kept busy with only cpu-bound work and io threads can be kept with io-bound work. This gives us full control over the concurrency. We could be confident that CPU threads are always available for compute work.

We cannot do this with python "yield".


If I had to guess, I think he's talking about coroutines that allow you to pass data as well as take it out.

Python's PEP 342, which expanded python generators into truer coroutines, has a decent discussion on the differences.

http://www.python.org/dev/peps/pep-0342/




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: