178 lines
7.1 KiB
Text
178 lines
7.1 KiB
Text
|
/**
|
||
|
\addtogroup sys
|
||
|
@{
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
\defgroup pt Protothreads
|
||
|
|
||
|
Protothreads are a type of lightweight stackless threads designed for
|
||
|
severly memory constrained systems such as deeply embedded systems or
|
||
|
sensor network nodes. Protothreads provides linear code execution for
|
||
|
event-driven systems implemented in C. Protothreads can be used with
|
||
|
or without an RTOS.
|
||
|
|
||
|
Protothreads are a extremely lightweight, stackless type of threads
|
||
|
that provides a blocking context on top of an event-driven system,
|
||
|
without the overhead of per-thread stacks. The purpose of protothreads
|
||
|
is to implement sequential flow of control without complex state
|
||
|
machines or full multi-threading. Protothreads provides conditional
|
||
|
blocking inside C functions.
|
||
|
|
||
|
The advantage of protothreads over a purely event-driven approach is
|
||
|
that protothreads provides a sequential code structure that allows for
|
||
|
blocking functions. In purely event-driven systems, blocking must be
|
||
|
implemented by manually breaking the function into two pieces - one
|
||
|
for the piece of code before the blocking call and one for the code
|
||
|
after the blocking call. This makes it hard to use control structures
|
||
|
such as if() conditionals and while() loops.
|
||
|
|
||
|
The advantage of protothreads over ordinary threads is that a
|
||
|
protothread do not require a separate stack. In memory constrained
|
||
|
systems, the overhead of allocating multiple stacks can consume large
|
||
|
amounts of the available memory. In contrast, each protothread only
|
||
|
requires between two and twelve bytes of state, depending on the
|
||
|
architecture.
|
||
|
|
||
|
\note Because protothreads do not save the stack context across a
|
||
|
blocking call, <b>local variables are not preserved when the
|
||
|
protothread blocks</b>. This means that local variables should be used
|
||
|
with utmost care - <b>if in doubt, do not use local variables inside a
|
||
|
protothread!</b>
|
||
|
|
||
|
|
||
|
Main features:
|
||
|
|
||
|
- No machine specific code - the protothreads library is pure C
|
||
|
|
||
|
- Does not use error-prone functions such as longjmp()
|
||
|
|
||
|
- Very small RAM overhead - only two bytes per protothread
|
||
|
|
||
|
- Can be used with or without an OS
|
||
|
|
||
|
- Provides blocking wait without full multi-threading or
|
||
|
stack-switching
|
||
|
|
||
|
Examples applications:
|
||
|
|
||
|
- Memory constrained systems
|
||
|
|
||
|
- Event-driven protocol stacks
|
||
|
|
||
|
- Deeply embedded systems
|
||
|
|
||
|
- Sensor network nodes
|
||
|
|
||
|
The protothreads API consists of four basic operations:
|
||
|
initialization: PT_INIT(), execution: PT_BEGIN(), conditional
|
||
|
blocking: PT_WAIT_UNTIL() and exit: PT_END(). On top of these, two
|
||
|
convenience functions are built: reversed condition blocking:
|
||
|
PT_WAIT_WHILE() and protothread blocking: PT_WAIT_THREAD().
|
||
|
|
||
|
\sa \ref pt "Protothreads API documentation"
|
||
|
|
||
|
The protothreads library is released under a BSD-style license that
|
||
|
allows for both non-commercial and commercial usage. The only
|
||
|
requirement is that credit is given.
|
||
|
|
||
|
\section authors Authors
|
||
|
|
||
|
The protothreads library was written by Adam Dunkels <adam@sics.se>
|
||
|
with support from Oliver Schmidt <ol.sc@web.de>.
|
||
|
|
||
|
\section pt-desc Protothreads
|
||
|
|
||
|
Protothreads are a extremely lightweight, stackless threads that
|
||
|
provides a blocking context on top of an event-driven system, without
|
||
|
the overhead of per-thread stacks. The purpose of protothreads is to
|
||
|
implement sequential flow of control without using complex state
|
||
|
machines or full multi-threading. Protothreads provides conditional
|
||
|
blocking inside a C function.
|
||
|
|
||
|
In memory constrained systems, such as deeply embedded systems,
|
||
|
traditional multi-threading may have a too large memory overhead. In
|
||
|
traditional multi-threading, each thread requires its own stack, that
|
||
|
typically is over-provisioned. The stacks may use large parts of the
|
||
|
available memory.
|
||
|
|
||
|
The main advantage of protothreads over ordinary threads is that
|
||
|
protothreads are very lightweight: a protothread does not require its
|
||
|
own stack. Rather, all protothreads run on the same stack and context
|
||
|
switching is done by stack rewinding. This is advantageous in memory
|
||
|
constrained systems, where a stack for a thread might use a large part
|
||
|
of the available memory. A protothread only requires only two bytes of
|
||
|
memory per protothread. Moreover, protothreads are implemented in pure
|
||
|
C and do not require any machine-specific assembler code.
|
||
|
|
||
|
A protothread runs within a single C function and cannot span over
|
||
|
other functions. A protothread may call normal C functions, but cannot
|
||
|
block inside a called function. Blocking inside nested function calls
|
||
|
is instead made by spawning a separate protothread for each
|
||
|
potentially blocking function. The advantage of this approach is that
|
||
|
blocking is explicit: the programmer knows exactly which functions
|
||
|
that block that which functions the never blocks.
|
||
|
|
||
|
Protothreads are similar to asymmetric co-routines. The main
|
||
|
difference is that co-routines uses a separate stack for each
|
||
|
co-routine, whereas protothreads are stackless. The most similar
|
||
|
mechanism to protothreads are Python generators. These are also
|
||
|
stackless constructs, but have a different purpose. Protothreads
|
||
|
provides blocking contexts inside a C function, whereas Python
|
||
|
generators provide multiple exit points from a generator function.
|
||
|
|
||
|
\section pt-autovars Local variables
|
||
|
|
||
|
\note
|
||
|
Because protothreads do not save the stack context across a blocking
|
||
|
call, local variables are not preserved when the protothread
|
||
|
blocks. This means that local variables should be used with utmost
|
||
|
care - if in doubt, do not use local variables inside a protothread!
|
||
|
|
||
|
\section pt-scheduling Scheduling
|
||
|
|
||
|
A protothread is driven by repeated calls to the function in which the
|
||
|
protothread is running. Each time the function is called, the
|
||
|
protothread will run until it blocks or exits. Thus the scheduling of
|
||
|
protothreads is done by the application that uses protothreads.
|
||
|
|
||
|
\section pt-impl Implementation
|
||
|
|
||
|
Protothreads are implemented using \ref lc "local continuations". A
|
||
|
local continuation represents the current state of execution at a
|
||
|
particular place in the program, but does not provide any call history
|
||
|
or local variables. A local continuation can be set in a specific
|
||
|
function to capture the state of the function. After a local
|
||
|
continuation has been set can be resumed in order to restore the state
|
||
|
of the function at the point where the local continuation was set.
|
||
|
|
||
|
|
||
|
Local continuations can be implemented in a variety of ways:
|
||
|
|
||
|
-# by using machine specific assembler code,
|
||
|
-# by using standard C constructs, or
|
||
|
-# by using compiler extensions.
|
||
|
|
||
|
The first way works by saving and restoring the processor state,
|
||
|
except for stack pointers, and requires between 16 and 32 bytes of
|
||
|
memory per protothread. The exact amount of memory required depends on
|
||
|
the architecture.
|
||
|
|
||
|
The standard C implementation requires only two bytes of state per
|
||
|
protothread and utilizes the C switch() statement in a non-obvious way
|
||
|
that is similar to Duff's device. This implementation does, however,
|
||
|
impose a slight restriction to the code that uses protothreads in that
|
||
|
the code cannot use switch() statements itself.
|
||
|
|
||
|
Certain compilers has C extensions that can be used to implement
|
||
|
protothreads. GCC supports label pointers that can be used for this
|
||
|
purpose. With this implementation, protothreads require 4 bytes of RAM
|
||
|
per protothread.
|
||
|
|
||
|
@{
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
/** @} */
|
||
|
/** @} */
|