
Protothreads: Lightweight Stackless Threads in C - signa11
http://dunkels.com/adam/pt/index.html
======
MrBuddyCasino
If "running multiple tasks on an embedded system that share a stack" sounds
interesting to you, Rust's RTFM is actually quite clever[0]. It only works on
ARM MCUs right now, but tasks are IRQ triggered, have a priority, memory safe,
deadlock-free and have very low overhead. Scheduling is done in hardware via
the interrupt controller.

[0] [https://github.com/japaric/cortex-m-
rtfm](https://github.com/japaric/cortex-m-rtfm)

------
photojosh
I wrote an entire I2C stack for an 8-bit microcontrolller using this
protothread library.

I would highly recommend using an RTOS instead. ;)

~~~
sgillen
any chance that code is up somewhere? Sounds interesting

~~~
baybal2
My guess is following: some people will put in doubt the rationale of using
threads for embedded development as such.

See, MCUs are much more interrupt driven than today's PCs, when it comes to
I/O, and don't have to run interface logic as it's usually done in hardware.

You can make a widget feel quite snappy and interactive even with very simple
linear code.

For the few tasks genuinely requiring real parallelism, or programming fancy
multicore MCUs, it will likely be that you will need more than just thread
separation, and it is better to do it properly than use hacks — in other
world, use process separation and let complexities be handled by the OS.

~~~
adrianratnapala
You can think of it as the reactor pattern. You only have one physical CPU so
all you are doing is churning through callback-based event handlers.

The callbacks in this case might be the literal interrupt handlers (in which
case your dispatcher "loop" is actually the hardware). Or else you make your
interrupt write a little bit of state that then gets dispatched by your actual
event loop.

Either way, you write responsive software by doing everything in short-running
callbacks.

I find it amusing that (some) sophisticated GUI and server framework for big
computers re-invent this.

~~~
photojosh
> doing everything in short-running callbacks

We call them "interrupt handlers." :)

~~~
adrianratnapala
It's amusing how you and alexhutcheson both corrected me in opposite
directions.

For reasons Alex explains, interrupt handlers have to be _really_ minimal. So
you often use them to set state that makes the main loop branch off into the
"real" handler.

But even then, you often don't want to wait a long time in that handler
either. That too becomes a short-running event handler (though not necessarily
a callback). Just not as extremely short running as it would have to be if did
it in the interrupt handler.

------
saagarjha
These seem to work with some clever (ab)use of switch statements:
[http://dunkels.com/adam/pt/expansion.html](http://dunkels.com/adam/pt/expansion.html)

~~~
greiskul
Wait, how does this work with local variables? Wouldn't the scope be destroyed
when you returned, how can it restore the stack if it doesn't save information
in the local continuation?

~~~
fjfaase
Indeed, there is a problem with local variables. The example relies on using
global variables. If you want multiple threads executing the same function, it
is not going to work. Also there is no solution for calling other functions
(and have them switch in the middle). A long time ago, I worked on an
implementation that does not have these limitations. See:
[http://www.iwriteiam.nl/Ha_cmt.html](http://www.iwriteiam.nl/Ha_cmt.html)

~~~
boomlinde
_> The example relies on using global variables._

It could as well rely on local variables with static lifetime, though.

 _> If you want multiple threads executing the same function, it is not going
to work._

That's true for the example, but in general the solution is to pass the
protothreads the thread-local state in a struct.

~~~
archgoon
> > The example relies on using global variables.

> It could as well rely on local variables with static lifetime, though.

For those following along, static local variables are the same thing as static
global variables[1], C just enforces a scoping restriction as to who can
access it at compile time. Most compilers will emit the same BSS section
entries for both (though they will mangle the symbol name). It's basically
compiler syntactic sugar for most compilers (though I'm sure the actual
standard itself allows for some weird exotic use cases where they get handled
completely differently).

[1] Static global variables are symbols that are restricted to a compilation
unit (as opposed to regular global variables that can be linked to from
another compilation unit). If you run `nm` on them, they'll all have a lower
case letter indicating it's type.

------
srfilipek
I hate protothreads with a passion.

Not because of what they do (it's a neat trick), but because it teaches bad
development practices to programmers.

I had a junior programmer "correct" me on some embedded code saying that all
my local variables should actually be declared static, because in their
experience "static is the way to go".

This "embedded" system was actually running Linux, using proper threading
semantics.

~~~
marssaxman
That's a matter of using the right tool for the environment, though, isn't it?
Someone looking at the world from the small-system, bare-metal embedded
development perspective might express similar feelings about garbage
collection, for teaching junior programmers to be unacceptably sloppy about
memory management.

I think it is simply to be expected that junior programmers will frequently
pick the wrong tool for the job, for any job in any context, as they have not
yet had time to master many tools and do not yet have enough experience to
understand the edges of their ignorance. (Presumably we would start thinking
of them as peers and calling them "senior programmers" at some point along
this journey of growth...)

------
huxingyi
Inspired by Protothreads, I implemented a similar framework 5 years ago, well,
not exactly a framework, just several lines of macros, which could be used in
general purpose not just embedded system (I am not sure if Protothreads can be
used in general purpose). I used it in product environment with libuv, here is
the github link:
[https://github.com/huxingyi/c-block](https://github.com/huxingyi/c-block)

------
networkimprov
Related: Go-style concurrency for C,
[http://libmill.org/](http://libmill.org/)

~~~
alexhutcheson
Although note that all libmill coroutines are mapped N:1 to OS threads, while
goroutines are mapped M:N to OS threads. In other words, libmill won’t let you
have run 2+ coroutines simultaneously on different OS cores, but Go will allow
that.

~~~
danappelxx
In fact, this means that writing concurrent code is easier as you don’t have
to worry about synchronizing access to shared state. It also means scaling
applications is straightforward, since you are forced to design for horizontal
scalability from the get go (similar to deploying node.js). Ive always found
it strange that Go has both channels and locks as synchronization primitives.

~~~
signa11
unless you start doing blocking read/write...

~~~
danappelxx
Libdill IO is nonblocking and calls returns a channel. Reading from the
channel yields the coroutine, similar to the await statement in some
languages.

~~~
signa11
oh cool ! does that happen for read/write to non-socket fd’s as well ?

~~~
danappelxx
Yeah, see the file descriptors section on this page
[http://libdill.org/documentation.html](http://libdill.org/documentation.html)

~~~
signa11
i did before posting here, and could not find anything that might have
indicated that i.e. non-socket-fd reads end up with coroutine like
semantics...

------
avodonosov
How does it work?

~~~
jlundberg
The following page describes the implementation details:
[http://dunkels.com/adam/pt/expansion.html](http://dunkels.com/adam/pt/expansion.html)

