Protothreads and C++

July 2008

Original article on blog.brush.co.nz

Part of my day job is embedded programming, and recently I’ve discovered Adam Dunkels’ lovely invention: protothreads. He calls them lightweight, stackless threads in C. And that they are.

What protothreads give you is the ability to write procedural, thread-style code, but without the overhead of real threads. The kind of thing embedded programmers normally use switch state machines for.

Protothread exampleThe other alternative is full-blown threads, but real threads certainly have their drawbacks. They each need their own stack, for one — and that’s often nasty in embedded systems, where you might only have 2 KB of RAM total. There’s the performance hit of context switching. There’s having to worry about sharing between threads, locking, etc.

Enter protothreads.

One of the cool things about them is that, behind the scenes, they break all the conventions (but not the commandments). Use of the legendary Duff’s device, macros that open braces but don’t close them … but they work, they’re fast and portable, and they give you pseudo-threads almost for free.

Because of how they work — perhaps it’s the same for any neat tool — there are a few gotchas:

You’ll want to read more about them and see some examples on Adam Dunkels’ website.

Protothreads in C++

But what about this C++ thing? Well, C++ is a more or less a superset of C, so protothreads will work as-is. But if we take C and sprinkle in a dash of ++, we can make them even tastier:

“Okay, so show us an example.” Fair call.

Below is a C++-style protothread that implements a simple packet protocol. Each packet has a sync byte, a length byte, n data bytes, and a checksum byte. Packets are only processed if they’re good and complete:

bool UartThread::Run()
{
    PT_BEGIN();

    while (true) {
        // wait for sync byte
        PT_WAIT_UNTIL(ReadByte(ch));
        if (ch == Sync) {
            // read length byte, ensure packet not too big
            PT_WAIT_UNTIL(ReadByte(ch));
            len = ch;
            if (len <= MaxLength) {
                // read n data bytes
                for (i = 0; i < len; i++) {
                    PT_WAIT_UNTIL(ReadByte(ch));
                    data[i] = ch;
                }
                // read checksum, dispatch packet if valid
                PT_WAIT_UNTIL(ReadByte(ch));
                if (ValidChecksum(data, len, ch))
                    Dispatch(data, len);
            }
        }
    }

    PT_END();
}

Not bad, eh? Even with comments it’s much shorter and sweeter than the equivalent state machine version (which, incidentally, is pretty much what the protothread macros expand to):

bool UartThread::Run()
{
    while (true) {
        switch (state) {
        case StateSync:
            if (!ReadByte(ch))
                return true;
            if (ch != Sync)
                break;
            state = StateLength;

        case StateLength:
            if (!ReadByte(ch))
                return true;
            len = ch;
            if (len > MaxLength) {
                state = StateSync;
                break;
            }
            i = 0;
            state = StateData;

        case StateData:
            while (i < len) {
                if (!ReadByte(ch))
                    return true;
                data[i] = ch;
                i++;
            }
            state = StateChecksum;

        case StateChecksum:
            if (!ReadByte(ch))
                return true;
            if (ValidChecksum(data, len, ch))
                Dispatch(data, len);
            state = StateSync;
        }
    }
}

So there you go. I know which version I’d rather write and maintain.

Feel free to use the Protothread.h header file I put together from Adam Dunkels’ C version — it should have all you need to get started. I’ve left a “protothread scheduler” as an exerciser for the reader, as it would be both simple and application-dependent.

Just a final word: I’m not suggesting protothreads are a replacement for threads — they’re not. But when you need the appearance of threads, or you’re dealing with embedded micros and don’t have screeds of RAM, give them a try.


Chris Woods has also implemented a version of protothreads in C++, but he’s taken a different approach for the Symbian OS.

Comments

name 26 Jul 2008, 02:14

Very cool! Do you have a simple example of how to turn local (static) variables into instance variables?

Thanks.

Ben 26 Jul 2008, 09:27

Sure. The example shown above has ch, len, etc as instance variables of the UartThread class.

There’s also the example below from the Protothread.h file I included:

class LEDFlasher : public Protothread
{
public:
    virtual bool Run();

private:
    ExpiryTimer _timer;
    uintf _i;
};

bool LEDFlasher::Run()
{
    PT_BEGIN();

    for (_i = 0; _i < 10; _i++)
    {
        SetLED(true);
        _timer.Start(250);
        PT_WAIT_UNTIL(_timer.Expired());

        SetLED(false);
        _timer.Start(750);
        PT_WAIT_UNTIL(_timer.Expired());
    }

    PT_END();
}

Here I’ve made the loop counter _i and the timer _timer instance variables (instead of static locals inside the Run function), so you can easily have multiple LEDFlashers. For example:

int main()
{
    LEDFlasher flasher1;
    LEDFlasher flasher2;

    while (flasher1.IsRunning() || flasher2.IsRunning()) {
        flasher1.Run();
        flasher2.Run();
    }
    return 0;
}

Lucy de Cobham 26 Jul 2008, 11:13

The equivalent state machine version is much easier to read and to reason.

Ben 26 Jul 2008, 11:39

I’m unsure why you think that, Lucy. I certainly find the short, procedural version easier to understand than the state machine — which jumps around, has multiple exit points, etc.

Also, you don’t necessarily need to “understand how protothreads work” on the inside to use them. The WAIT_UNTIL macro simply does what it sounds like — waits until the given condition is true.

Aaron 17 Nov 2012, 14:39

Ben wrote:

Also, you don’t necessarily need to “understand how protothreads work”

This is the core value of coroutine mechanisms – they abstract away implementation mechanics. When someone expresses anxiety over the “gotos” involved, I point out that therevare “gotos” embedded in every procedure call, if statement, or loop. What’s important is the structure of the program at a high level, and these techniques lead to better structure, i my experience.

Bob 18 Sep 2014, 02:41

Do you have any examples of inter-pthread communication?