Protothreads: a Lightweight Threading Solution for Resource-constrained Systems

The protothreads library provides a lightweight, stackless threading solution targeted for resource-constrained embedded systems. Protothreads provides an event-driven system with a blocking context, without the overhead of per-thread stacks. And I'm serious when I say "lightweight": each protothread requires only 2 bytes of memory! The library is implemented in pure C and can be used with any architecture.

Unlike traditional multithreading solutions, the application is responsible for handling the scheduling protothreads. Protothreads are driven by repeated calls to a protothread function (from a runloop, for example). Each time the a protothread function is invoked, the protothread will run until it blocks or exits.

Protothreads is nicely coupled with event-driven embedded systems or systems whose memory resources are too constrained for an RTOS.

Important Usage Notes

It is important to keep in mind the following usage notes:

  1. Avoid using local variables within a protothread, because they do not save stack context across a blocking call. The local variable values are not preserved.
  2. A protothread cannot use a switch statement due to the implementation, which takes advantage of C switch behavior.
  3. A protothread runs within a single C function and cannot span over other functions.
  4. A protothread may call normal C functions, but cannot block inside a called function. If you wish to call a blocking function, you must spawn a separate protothread. This provides an explicit indication of which functions may block and which functions are not able to.
  5. Threads run on the same stack and use stack unwinding for context switching.

Protothread Examples

Protothreads are used by the Aachen Robotics Club in the modm and xpcc frameworks. The protothread website also contains examples.

Included below is a very simple protothreads example. We have one thread which waits until the counter variable reaches 1000, then it prints a string and resets the count. The main() function invokes the thread in a runloop and increases the count.

#include "pt.h"

static int counter;
static struct pt example_pt;

static
PT_THREAD(example(struct pt *pt))
{
  PT_BEGIN(pt);

  while(1) {
    PT_WAIT_UNTIL(pt, counter == 1000);
    printf("Threshold reached\n");
    counter = 0;
  }

  PT_END(pt);
}

int main(void)
{
  counter = 0;
  PT_INIT(&example_pt);

  while(1) {
    example(&example_pt);
    counter++;
  }
  return 0;
}

Further Reading