jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
Protothreads |
Posted: Tue Feb 24, 2015 2:13 pm |
|
|
Below is a ported and modified version of Adam Dunkel's protothread library. His original idea and library can be found here:
http://dunkels.com/adam/pt/
Protothreads are a stackless, OS-less, pseudo thread construct that allows cooperative like scheduling between each other. Be aware that since they are stackless, automatic variables within threads are not saved between each run.
CCS Does not support standard local continuations like most ANSI compilers do. However, the CCS compiler does provide label_address() and goto_address() which can be used to implement the same type of logic (though at a steeper ROM cost).
There were some functional design changes as well, which are documented in the code. If you are opposed to them, feel free to update your version with the logic from Adam Dunkel's site.
Finally, I defaulted the implementation to rely on reference parameters instead of pointers. This is because the indirect accessing of the pointers led to much larger ROM usage. Moving to reference parameters reduces the ROM cost as long as you aren't calling the same thread function in more than one place (then there are tradeoffs depending on the size of the thread and number of times it is called, etc.). If you absolutely hate references or find they are taking up more ROM than pointers would, there is a #define in the code that can be commented out so that pointers can be used. Just make sure to update all your thread prototypes and calls to match.
This was tested using PCWHD v5.025 on the PIC24FJ64GA004. It has not been tested on other platforms. I did not individually test out all the comment examples, so if any have a major issue let me know.
protothreads.h
Code: |
/******************************************************************************
* NAME: protothreads.h
* AUTH: Jeremiah (Reworked from Adam Dunkel's code)
* DESC: Provides a stackless, OS-less, low RAM implementation of cooperative
* multithreading for the CCS compiler environment. Based off of the
* protothreads library provided by Adam Dunkel, but with modifications
* for the CCS compiler environment. Specifically:
* 1. CCS doesn't support local continuations, so compiler extensions
* are used instead. label_address() and goto_address()
* 2. Removal of do{}while() loops as they generate compiler warnings
* and are not needed for CCS.
* 3. Changed PT_END() and PT_EXIT() to not reset the local
* continuation but instead remember the thread finished.
* 4. Re-orged a few macros to better optimize with CCS.
* 5. Changed PT_YIELD_FLAG=0 to PT_YIELD_FLAG=PT_YIELD_FLAG in the
* PT_END() macro
* 6. Removed PT_THREAD() and replaced it with pt_ret_t
* 7. Added (and defaulted to) the use of reference parameters since
* those provide less code in most general scenarios. A #define
* can be commented out to go back to pointers.
* Copyright notice from Adam Dunkels code provided near the end of the
* file.
*
* TYPES:
* pt_t - Protothread context type
* pt_ret_t - Protothread return type
* PT_WAITING - Thread is waiting/blocking
* PT_YIELDED - Thread yielded
* PT_EXITED - Thread ended abnormally
* PT_ENDED - Thread ended normally
* pt_sem_t - Protothread semiphore type
*
* MACROS:
* PT_INIT() - Must be called prior to scheduling a thread
* PT_BEGIN() - Must be called at the beginning of a protothread.
* PT_END() - Must be called at the end of a protothread (once)
* PT_WAIT_WHILE() - Used to block a thread while a condition is TRUE
* PT_WAIT_UNTIL() - Used to block a thread until a condition is TRUE
* PT_YIELD() - Yields the current thread
* PT_YIELD_WHILE() - Yields current thread while a condition is TRUE
* PT_YIELD_UNTIL() - Yields current thread until a condition is TRUE
* PT_RESTART() - Restarts the current thread
* PT_EXIT() - Exits the current thread
* PT_SCHEDULE() - Schedules a thread from a non-thread method
* PT_WAIT_THREAD() - Spawns a child thread and waits on it to end
* PT_SPAWN() - Same as PT_WAIT_THREAD() but with PT_INIT()
* PT_SEM_INIT() - Initializes a semiphore
* PT_SEM_WAIT() - Waits on a semiphore signal (decrements count)
* PT_SEM_SIGNAL() - Sends a semiphore signal (increments count)
*
* COMPILED WITH: CCS PCWHD Revision 5.025
*
* HISTORY:
* 02/20/2014 - JHB - Creation
******************************************************************************/
#ifndef PROTOTHREADS_H
#define PROTOTHREADS_H
#nolist
///////////////////////////////////////////////////////////////////////////////
//***************** File Option Settings ************************************//
///////////////////////////////////////////////////////////////////////////////
//CCS specific - for use with reference parameters vs pointer parameters
#define PT_USE_REFERENCES //comment this line out to use pointers
#if defined(PT_USE_REFERENCES)
#define PT_STRUCT_ELEM(st, e) ((st).e)
#else
#define PT_STRUCT_ELEM(st, e) ((st)->e)
#endif
///////////////////////////////////////////////////////////////////////////////
//***************** Local Continuations (Compiler Specific) *****************//
///////////////////////////////////////////////////////////////////////////////
//This section sets up some macros used by the protothreads interface. These
//macros should not be used directly. They are merely tools for the
//protothreads interface.
//Variable typedef
#if defined(__PCH__) || defined(__PCD__)
typedef unsigned int32 lc_t;
#else
typedef unsigned int16 lc_t;
#endif
//Used to reset the local continuation
#define LC_INIT(lc) lc = 0
//Place in PT_BEGIN()
#define LC_RESUME(lc) if(0 != lc){goto_address(lc);}
//Used to create continuations
#define LC_CONCAT_INNER(s1,s2) s1##s2
#define LC_CONCAT(s1,s2) LC_CONCAT_INNER(s1,s2)
#define LC_SET(lc) \
LC_CONCAT(LC_LABEL, __LINE__): \
(lc) = label_address(LC_CONCAT(LC_LABEL,__LINE__))
//Place in PT_END()
#define LC_END(lc)
///////////////////////////////////////////////////////////////////////////////
//***************** Protothreads Interface **********************************//
///////////////////////////////////////////////////////////////////////////////
//This is the meat of the file. Macros from here will be used to create and
//manage protothreads.
//Variable Typedefs
typedef struct{
lc_t lc;
} pt_t;
typedef enum{
PT_WAITING = 0,
PT_YIELDED = 1,
PT_EXITED = 2,
PT_ENDED = 3
} pt_ret_t;
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_INIT
// DESC: Initializes the control structure for a protothread. Must be called
// prior to calling the thread.
// IN: pt - protothread control structure
// OUT: NONE
// NOTE: Example:
// pt_ret_t thread(pt_t &pt);
// void main(){
// pt_t pt;
// PT_INIT(pt);
// while(TRUE){PT_SCHEDULE(thread(pt));}
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_INIT(pt) LC_INIT(PT_STRUCT_ELEM(pt,lc))
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_BEGIN
// DESC: Starts the control code for a thread. Must be called after variable
// declarations and before function program code
// IN: pt - protothread control structure
// OUT: NONE
// NOTE: Example:
// pt_ret_t thread(pt_t &pt){
// static int variable;
// PT_BEGIN(pt);
// variable = 10;
// //do stuff here
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_BEGIN(pt) int1 PT_YIELD_FLAG = 1; LC_RESUME(PT_STRUCT_ELEM(pt,lc))
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_END
// DESC: Ends the control code for a thread. Must be called at the end of
// of a protothread. Leaves the tread with the PT_ENDED status.
// IN: pt - protothread control structure
// OUT: NONE
// NOTE: Example:
// pt_ret_t thread(pt_t &pt){
// PT_BEGIN(pt);
// //do stuff here
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_END(pt) LC_SET(PT_STRUCT_ELEM(pt,lc)); \
LC_END(PT_STRUCT_ELEM(pt,lc)); PT_YIELD_FLAG = PT_YIELD_FLAG;\
return PT_ENDED
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_WAIT_WHILE
// DESC: Blocks the current thread while a supplied condition is true
// IN: pt - protothread control structure
// condition - condition that causes blocking
// OUT: NONE
// NOTE: Example:
// int g_variable = 0;
// pt_ret_t thread(pt_t &pt){
// PT_BEGIN(pt);
// PT_WAIT_WHILE(0 == g_variable);
// //do stuff here
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_WAIT_WHILE(pt, condition) \
LC_SET(PT_STRUCT_ELEM(pt,lc)); if(condition) { return PT_WAITING;}
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_WAIT_UNTIL
// DESC: Blocks the current thread until a supplied condition becomes true
// IN: pt - protothread control structure
// condition - condition that ends blocking
// OUT: NONE
// NOTE: Example:
// int g_variable = 0;
// pt_ret_t thread(pt_t &pt){
// PT_BEGIN(pt);
// PT_WAIT_UNTIL(0 != g_variable);
// //do stuff here
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_WAIT_UNTIL(pt, condition) PT_WAIT_WHILE(pt, !(condition))
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_YIELD
// DESC: Yields the current thread
// IN: pt - protothread control structure
// OUT: NONE
// NOTE: Example:
// pt_ret_t thread(pt_t &pt){
// static int16 i;
// PT_BEGIN(pt);
// for(i=0; i<10000; i++){
// //do something
// PT_YIELD(pt);
// }
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_YIELD(pt) \
PT_YIELD_FLAG = 0; \
LC_SET(PT_STRUCT_ELEM(pt,lc)); \
if(0 == PT_YIELD_FLAG){ return PT_YIELDED; }
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_YIELD_WHILE
// DESC: Yields the current thread while a supplied condition is true. Always
// yeilds at least once.
// IN: pt - protothread control structure
// condition - condition that causes yielding
// OUT: NONE
// NOTE: Example:
// int g_variable = 0;
// pt_ret_t thread(pt_t &pt){
// PT_BEGIN(pt);
// PT_YIELD_UNTIL(0 != g_variable);
// //do stuff here
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_YIELD_WHILE(pt, condition) \
PT_YIELD_FLAG = 0; \
LC_SET(PT_STRUCT_ELEM(pt,lc)); \
if((0 == PT_YIELD_FLAG) || (condition)){ return PT_YIELDED; }
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_YIELD_UNTIL
// DESC: Yields the current thread until a supplied condition becomes true.
// Always yeilds at least once.
// IN: pt - protothread control structure
// condition - condition that ends yielding
// OUT: NONE
// NOTE: Example:
// int g_variable = 0;
// pt_ret_t thread(pt_t &pt){
// PT_BEGIN(pt);
// PT_YIELD_WHILE(0 != g_variable);
// //do stuff here
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_YIELD_UNTIL(pt, condition) PT_YIELD_WHILE(pt, !(condition))
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_RESTART
// DESC: Restarts the current thread back to the beginning
// IN: pt - protothread control structure
// OUT: NONE
// NOTE: Example:
// BOOLEAN function();
// pt_ret_t thread(pt_t &pt){
// PT_BEGIN(pt);
// if(!function()){
// PT_RESTART(pt);
// }
// //do stuff
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_RESTART(pt) PT_INIT(pt); return PT_WAITING
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_EXIT
// DESC: Leaves the current thread with the PT_EXITED status
// IN: pt - protothread control structure
// OUT: NONE
// NOTE: Example:
// BOOLEAN function();
// pt_ret_t thread(pt_t &pt){
// PT_BEGIN(pt);
// if(!function()){
// PT_EXIT(pt);
// }
// //do stuff
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_EXIT(pt) LC_SET(PT_STRUCT_ELEM(pt,lc)); return PT_EXITED
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_SCHEDULE
// DESC: Calls a thread from a non-thread function and compares it to
// the current status.
// IN: thread - protothread to call
// OUT: BOOLEAN - True if not PT_EXIT or PT_END
// NOTE: Example:
// pt_ret_t thread(pt_t &pt);
// void main(){
// pt_t pt;
// PT_INIT(pt);
// while(PT_SCHEDULE(thread(pt)));
// while(TRUE);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_SCHEDULE(thread) ((thread) < PT_EXITED)
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_WAIT_THREAD
// DESC: Calls a child thread from a protothread function and waits until
// the child thread ends. Must call PT_INIT() on child prior to calling
// thread.
// IN: pt - parent protothread control structure
// thread - protothread to call
// OUT: NONE
// NOTE: Example:
// pt_ret_t childThread(pt_t &pt);
// int g_variable = 0;
// pt_ret_t thread(pt_t &pt){
// pt_t child;
// PT_BEGIN(pt);
// PT_WAIT_UNTIL(0 != g_variable);
// //do stuff here
// PT_INIT(child);
// PT_WAIT_THREAD(pt,childThread(child));
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE(pt,PT_SCHEDULE(thread))
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_SPAWN
// DESC: Initializes a child thread context structure, calls the child
// thread from a protothread function, and waits until the child thread
// ends.
// IN: pt - parent protothread control structure
// child - child protothread control structure
// thread - protothread to call
// OUT: NONE
// NOTE: Example:
// pt_ret_t childThread(pt_t &pt);
// int g_variable = 0;
// pt_ret_t thread(pt_t &pt){
// pt_t child;
// PT_BEGIN(pt);
// PT_WAIT_UNTIL(0 != g_variable);
// //do stuff here
// PT_SPAWN(pt,child,childThread(child));
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_SPAWN(pt, child, thread) PT_INIT(child); PT_WAIT_THREAD(pt,thread)
///////////////////////////////////////////////////////////////////////////////
//***************** Semiphore Interface *************************************//
///////////////////////////////////////////////////////////////////////////////
//Variable typedefs
typedef struct{
unsigned int16 count;
} pt_sem_t;
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_SEM_INIT
// DESC: Initializes the a semiphore for a protothread.
// IN: s - protothread semiphore structure
// c - initial count
// OUT: NONE
// NOTE: Example:
// pt_ret_t thread(pt_t &pt, pt_sem_t &sem);
// void main(){
// pt_t pt;
// pt_sem_t sem;
// PT_INIT(pt);
// PT_SEM_INIT(sem,0);
// while(TRUE){PT_SCHEDULE(thread(pt,sem));}
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_SEM_INIT(s, c) PT_STRUCT_ELEM(s,count) = c
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_SEM_WAIT
// DESC: Forces the current thread to block until the semiphore has a non
// zero count. Decrements the count afterwards.
// IN: pt - protothread control structure
// s - protothread semiphore structure
// OUT: NONE
// NOTE: Example:
// pt_ret_t thread(pt_t &pt, pt_sem_t &sem){
// PT_BEGIN(pt);
// PT_SEM_WAIT(pt,sem);
// //consume something
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_SEM_WAIT(pt, s) \
PT_WAIT_UNTIL(pt, PT_STRUCT_ELEM(s,count) > 0); \
--(PT_STRUCT_ELEM(s,count))
///////////////////////////////////////////////////////////////////////////////
// NAME: PT_SEM_SIGNAL
// DESC: Increments the semiphore count.
// IN: pt - protothread control structure
// s - protothread semiphore structure
// OUT: NONE
// NOTE: Example:
// int g_variable = 0;
// pt_ret_t thread(pt_t &pt, pt_sem_t &sem){
// PT_BEGIN(pt);
// while(0 == g_variable){
// //produce some stuff
// }
// PT_SEM_SIGNAL(pt,sem);
// PT_END(pt);
// }
///////////////////////////////////////////////////////////////////////////////
#define PT_SEM_SIGNAL(pt, s) ++(PT_STRUCT_ELEM(s,count))
/*
Copyright (c) 2004-2005, Swedish Institute of Computer Science.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the Institute nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS `AS IS' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Author: Adam Dunkels
*/
#list
#endif
|
|
|