12 comments

[ 2.5 ms ] story [ 51.7 ms ] thread
Today I learned: "PuTTY is a Win32 Telnet and SSH client. The SSH protocol code contains real-life use of this coroutine trick. As far as I know, this is the worst piece of C hackery ever seen in serious production code."
Wow,no kidding https://github.com/Yasushi/putty/blob/31a2ad775f393aad1c31a9...

It's beautiful and incredibly scary at the same time.

just wow, from the snippet you linked:

  *  - right-click ssh.c in the FileView
  *  - click Settings
  *  - select the C/C++ tab and the General category
  *  - under `Debug info:', select anything _other_ than `Program
  *    Database for Edit and Continue'.
Right, so in order to compile the thing you need to click "something other than". That doesn't sound deterministic.
That's ensuring the compiler obeys some behaviors of an ANSI C compiler when debugging in MS Visual Studio. That strikes me as the kind of practice that would improve determinism. The explanation is given on the line before your quote.

That said, I can't believe this exists in the wild.

Some extra fun (and obscene) things along this line:

- Use __LINE__ to make it so you don't have to manually specify the continuation number (with the caveat that you can't have more than one on a line).

- Use computed goto and labels rather than a switch and you can make it work even inside a switch (&&label to take the address, goto *addr to goto). This is a gcc extension.

- Use clang blocks to save state and return the block for continuation, saving you from needing to use static functions. Perhaps you could also use C++ lambdas along the same line, but I'm not sure if they mix well with the &&label thing.

In embedded land, you can just make something that works like setjmp and memcopy the stack off to someplace convenient.

Super useful if you want something resembling modern programming in an embedded context. It is a bit better than callback hell, but you do end up using memory for all those saved off stacks, so that limits how many co-routines can be in play at once. Thankfully embedded projects tend to have small stacks. :)

I'm pretty sure the code in the article could use __COUNTER__ followed by __COUNTER__-1 in the macro to avoid the __LINE__ collision and allow more than one yield on the same line.
(comment deleted)
Let me rant about a bigger point: What are coroutines conceptually? Well, it's a "thing" that we use in order to express sequential code (I'm sure there is more abstract definition).

What is a difference between Erlang Proc, Golang Goroutine, Operating System Thread, and this kind of Duff's device - not much if you think about it. It's all about sequential code with a bit of context (variables) attached - the closure.

The only difference between a thread and asynchronous programming is how you deal with the context - is it explicit or implicit?

In this Duff's device, the context is passed explicitly. It's an (arguably nice) syntactic sugar over a trivial state machine.

The main point here is - how large is the context? If that's a few variables, sure you can pack them on a struct, or just async programming. If the context is somewhat non-trivial, having it implicit makes sense. If the context is really large you probably _must_ use OS / programming language help.

The problem - the larger the context, the costlier the context switch. It would be nice to have some kind of magic that would be able to chose the optimal coroutines hardware implementation based on the size of context.

> The only difference between a thread and asynchronous programming is how you deal with the context

Coroutines are not threads. Each serve vastly different purposes. Coroutines are basically an easier way of expressing certain kinds of state machines. They're great for games when you need a character or other game object to progress through different states. They free you from 3-10x boilerplate that would be required if you had to write and manipulate a state machine directly. They're not related to threading whatsoever.

And people are giving shit to VB for having a GoTo statement!