weAut_01 / weAutSys    R 2.2.1
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines
Modules | Files | Data Structures | Defines | Typedefs
+ + pt -- Adam Dunkels' lightweight Protothreads + +
+ + + weAutSys -- software incorporated from other sources + + +

Overview

weAutSys uses Protothreads as its multi-threading base for all automation / user cycles as well as for system, alarm and I/O handling threads. Adam Dunkels' invention is very lightweight in terms of RAM and processor time consumptions and thus best fit for ยต-controller based automation systems.

In weAutSys Protothreads is used with only minimal modifications / additions to Adam Dunkels' ingenious original.

Note:
Uppercase Protothreads means the ingenious basic software approach (by Adam Dunkel) while lower case protothread relates to a concrete (weAutSys') thread, thread data-structure or thread function.

In (mostly) Adam Dunkels' words (3 June 2006):
Protothreads are extremely lightweight stackless threads designed for severely memory constrained systems such as small embedded systems or sensor network nodes. Protothreads can be used with or without an underlying operating system.

Protothreads 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.

Main features:

The Protothreads library is released under an open source BSD-style license that allows for both non-commercial and commercial usage. The only requirement is that credit is given.
The Protothreads library was written by Adam Dunkels adam@.nosp@m.sics.nosp@m..se with support from Oliver Schmidt ol.sc.nosp@m.@web.nosp@m..de.


Modules

 Local continuations

Files

file  pt.h
 

Protothreads implementation.


Data Structures

struct  pt
 A protothread's (raw) data structure. More...

Defines

#define PT_ENDED
 thread has finished
#define PT_EXITED
 thread has finished
#define PT_WAITING   0
 thread paused due to a condition
#define PT_YIELDED
 Thread paused.
#define ptfnct_t
 The return type of a protothread function.

Initialization

#define PT_INIT(pt)
 Initialise a protothread.

Declaration and definition

#define PT_BEGIN(pt)
 Declare the start of a protothread inside the protothread function.
#define PT_END(pt)
 Declare the end of a protothread.

Blocked wait

#define PT_WAIT_UNTIL(pt, condition)
 Block and wait until condition is true.
#define PT_WAIT_WHILE(pt, cond)
 Block and wait while condition is true.

Hierarchical protothreads

#define PT_WAIT_THREAD(pt, thread)
 Block and wait until a child protothread completes.
#define PT_SPAWN(pt, child, thread)
 Spawn a child protothread and wait until it exits.

Exiting and restarting

#define PT_RESTART(pt)
 Restart the protothread.
#define PT_EXIT(pt)
 Exit the protothread.
#define PT_LEAVE(pt)
 End the protothread.

Yielding from a protothread

#define PT_YIELD(pt)
 Yield from the current protothread.
#define PT_YIELD_UNTIL(pt, cond)
 Yield from the protothread until a condition occurs.
#define PT_YIELD_WHILE(pt, condition)
 Yield while a condition is true.
#define PT_WAIT_ASYIELD_WHILE(pt, condition)
 Wait while a condition is true mimicked as yield.
#define PT_OR_YIELD_HERE(pt)
 Yield only if a previous conditional wait did not.
#define PT_OR_YIELD_REENTER()
 Yield only if a previous conditional wait did not and re-enter that.

Typedefs

typedef struct pt pt_t
 A protothread's (raw) data structure as type.

Define Documentation

#define ptfnct_t

The return type of a protothread function.

/note Within the PT_BEGIN PT_END braces a protothread function must not return directly (forgetting rare exceptions with lc-addrlabels). The return is done by Protothread operations.

Return values are PT_WAITING, PT_YIELDED, PT_EXITED and PT_ENDED.

The following holds:
a) PT_WAITING < PT_YIELDED < PT_EXITED < PT_ENDED
b) There is no real semantic difference between PT_EXITED and PT_ENDED

The knowledge a) is useful in evaluating return values.

The difference of PT_EXITED and PT_ENDED is almost insignificant:
The macro PT_END which returns PT_ENDED must appear exactly once at the end of a protothread function because it is the closing bracket to PT_BEGIN.
The macro PT_EXIT returning PT_EXITED may on the other hand appear as often as useful.
The effect of PT_EXIT and PT_END is exactly the same:
The thread does end meaning its next schedule will "land" / start thread work after PT_BEGIN. (Hence you will hardly find any code that won't OR PT_EXITED and PT_ENDED in concerning conditionals.
An extra macro PT_LEAVE returns PT_ENDED and can be used anywhere (saving jumps to PT_END just for the intended return value).

Examples:
main.c.
#define PT_YIELDED

Thread paused.

Yielding is mostly done as courtesy to other threads or to obey CPU usage trime limits. As Protothreads are non-preemptive this is the responsibility of every single thread function!

Insofar the semantic difference to waiting is the (extra) reason to pause the thread, lest it runs too long. In that sense yielding with an condition should yield at least once no matter the condition's state — as does the original Protothread.

On the other hand weAutSys needed a to convey the (semantic) difference on the reason for a wait on a condition:

  • condition will fulfill automatically or
  • the calling software must actively take action.

Herefore we use PT_YIELDED vs. PT_WAITING to transport this information via the (child) thread functions return value.

#define PT_EXITED

thread has finished

See also:
ptfnct_t
Examples:
main.c.
#define PT_ENDED

thread has finished

See also:
ptfnct_t
Examples:
main.c.
#define PT_INIT (   pt)

Initialise a protothread.

Initialisation must be done prior to starting to execute the protothread.

Parameters:
ptA pointer to the protothread control structure.
See also:
PT_SPAWN()
#define PT_BEGIN (   pt)

Declare the start of a protothread inside the protothread function.

This macro is used to declare the starting point of a protothread. It should be placed at or near the start of the function in which the protothread runs respectively is implemented. PT_BEGIN() is the gridiron to the last set (left, yield, block) re-entry point.

All statements above the PT_BEGIN() invocation will be executed each time the protothread is scheduled.

The statements below PT_BEGIN() will be executed at the first entry into an initialised or restarted protothread.

Parameters:
ptthe pointer to the protothread control structure.
Examples:
main.c.
#define PT_END (   pt)

Declare the end of a protothread.

This macro is used for declaring that a protothread ends. It must always be used together with a matching PT_BEGIN() macro. A statement below won't be reached (except when labeled and gone to).

Parameters:
ptthe pointer to the protothread control structure.
Examples:
main.c.
#define PT_WAIT_UNTIL (   pt,
  condition 
)

Block and wait until condition is true.

This macro blocks the protothread until the specified condition is true.

Parameters:
ptpointer to the protothread control structure
conditionthe condition (a boolean expression)
Examples:
main.c.
#define PT_WAIT_WHILE (   pt,
  cond 
)

Block and wait while condition is true.

This function blocks and waits while condition is true. See PT_WAIT_UNTIL().

Parameters:
ptpointer to the protothread control structure
condthe condition (a boolean expression)
#define PT_WAIT_THREAD (   pt,
  thread 
)

Block and wait until a child protothread completes.

This macro schedules a child protothread. The current protothread will block until the child protothread completes.

Note:
The child protothread must be manually initialised with the PT_INIT() function before this function is used.
Parameters:
ptA pointer to the (calling) protothread control structure.
threadThe child protothread with arguments
See also:
PT_SPAWN()
#define PT_SPAWN (   pt,
  child,
  thread 
)

Spawn a child protothread and wait until it exits.

This macro spawns a child protothread and waits until it exits. The macro can only be used within a protothread.

Parameters:
ptA pointer to the (calling) protothread control structure
childA pointer to the child protothread's control structure
threadThe child protothread's function call with arguments
#define PT_RESTART (   pt)

Restart the protothread.

This macro will block once and cause the running protothread to restart its execution at the place after PT_BEGIN().

Parameters:
ptA pointer to the protothread control structure
#define PT_EXIT (   pt)

Exit the protothread.

This macro causes the protothread to exit. If the protothread was spawned by another protothread, the parent protothread will become unblocked and can continue to run.

Parameters:
ptA pointer to the (raw) protothread control structure
Examples:
main.c.
#define PT_LEAVE (   pt)

End the protothread.

This macro causes the protothread to end normally. It has the same effect as PT_EXIT except returning PT_ENDED. Insofar it is equivalent to falling through to the end of the PT_BEGIN .. PT_END brace.

Parameters:
ptA pointer to the (raw) protothread control structure
#define PT_YIELD (   pt)

Yield from the current protothread.

This function will yield the protothread, thereby allowing other processing to take place. As Protothreads are non-preemptive by nature unconditional yielding is one (and the only) way to dependably subdivide longer running tasks into junks of maximum allowed execution time.

Parameters:
ptA pointer to the protothread control structure
See also:
PT_YIELD_UNTIL
PT_WAIT_ASYIELD_WHILE
Examples:
main.c.
#define PT_YIELD_UNTIL (   pt,
  cond 
)

Yield from the protothread until a condition occurs.

This function will yield the protothread, until the specified condition evaluates to true.

The thread yields at least once, even if the condition cond is true at first arrival. This is a bit counter-intuitive but may be wanted in some circumstances; i.e if a cut / subdivision of the task's runtime is due anyway.

If no yield should take place if the condition is yet true (behaviour like PT_WAIT_UNTIL but with other returned state) use PT_WAIT_ASYIELD_WHILE with the inverse condition.

Parameters:
ptA pointer to the protothread control structure
condThe condition.
See also:
PT_YIELD
#define PT_YIELD_WHILE (   pt,
  condition 
)

Yield while a condition is true.

This macro yields the protothread until and only if the specified condition is true.

  • The difference to PT_WAIT_WHILE is the returned state.
    Semantically this is a wait returning PT_YIELDED.
  • The difference to PT_YIELD_UNTIL is the more intuitive behaviour; i.e not yielding or just going ahead if condition is false from start.
Parameters:
ptpointer to the protothread control structure
conditionthe condition (a boolean expression).
See also:
PT_OR_YIELD_HERE
#define PT_WAIT_ASYIELD_WHILE (   pt,
  condition 
)

Wait while a condition is true mimicked as yield.

This macro yields the protothread until and only if the specified condition is true.

  • The difference to PT_WAIT_WHILE is the returned state.
    Semantically this is a wait returning PT_YIELDED.
  • The difference to PT_YIELD_WHILE is the behaviour of not yielding or just going ahead if condition is false from start.
Parameters:
ptpointer to the protothread control structure
conditionthe condition (a boolean expression).
See also:
PT_OR_YIELD_HERE
Examples:
main.c.
#define PT_OR_YIELD_HERE (   pt)

Yield only if a previous conditional wait did not.

This macro yields the protothread if and only if a previous conditional wait or wait_as_yield yield did not do so and just went ahead.

Parameters:
ptpointer to the protothread control structure
See also:
PT_WAIT_ASYIELD_WHILE
PT_OR_YIELD_REENTER()
#define PT_OR_YIELD_REENTER ( )

Yield only if a previous conditional wait did not and re-enter that.

This macro yields if and only if a previous conditional wait or wait_as_yield macro did not do so and just went ahead. In that case this yields the protothread.

Contrary to the else similar PT_OR_YIELD_HERE the resume point for the next schedule is not at this macro's (PT_OR_YIELD_REENTER) call but (back) at the previous block or yield. The rationale lies in those cases where that previous block or yield macro's conditions must be rechecked or its other (side) effects have to be repeated.

Hint: As this behavior is not intelligibly to the reader of the source code a clarifying comment is strongly recommended:

      PT_YIELD_OUT_SPACE(&cliThread->pt, 31, cliThread->repStreams); // (A)
      // if and only if (A) didn't yield, do yield here and now and
      PT_OR_YIELD_REENTER(); // re-enter at (A) above on next schedule
See also:
PT_WAIT_ASYIELD_WHILE
PT_OR_YIELD_HERE
Examples:
main.c.