协程Protothread
Posted Frey_Liu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了协程Protothread相关的知识,希望对你有一定的参考价值。
Protothread在contiki os 的 process中有广泛的应用!contiki 的作者Adam Dunkels,同时也是uip,lwip的作者。
Contiki Protothread宏
// contiki OS protothread
// path: contiki\\core\\sys\\pt.h
struct pt
lc_t lc; // 定义了一个存放行号的变量, lc_t只是一个短整形的类型
;
#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED 2
#define PT_ENDED 3
PT_INIT(pt) // 宏语句展开后的实际代码如下
(pt)->lc = 0;
PT_BEGIN(pt); // 宏语句展开后的实际代码如下
char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) ; switch((pt)->lc) case 0: ;
PT_EXIT(pt); // 宏语句展开后的实际代码如下
do \\
(pt)->lc = 0; \\
return PT_EXITED; \\
while(0);
/// PT_WAIT_UNTIL宏里在当前行放置一个case, 这样就跟前面的switch组合起来了.
/// 然后判断condition是否满足, 不满足就return.
/// 下一次进这个函数的时候, 直接从PT_BEGIN的地方, 跳转到这里.
PT_WAIT_UNTIL(pt, condition); // 宏语句展开后的实际代码如下
do \\
(pt)->lc = __LINE__; case __LINE__: ; \\
if(!(condition)) \\
return PT_WAITING; \\
\\
while(0);
PT_RESTART(pt); // 宏语句展开后的实际代码如下
do \\
(pt)->lc = 0; \\
return PT_WAITING; \\
while(0);
PT_SPAWN(pt, child, thread); // 宏语句展开后的实际代码如下
do \\
((child))->lc = 0;; \\
PT_WAIT_WHILE((pt), ((thread) < PT_EXITED)); \\ // similar to PT_WAIT_UNTIL
while(0);
PT_YIELD(pt); // 宏语句展开后的实际代码如下
do \\
PT_YIELD_FLAG = 0; \\
(pt)->lc = __LINE__; case __LINE__: ; \\
if(PT_YIELD_FLAG == 0) \\
return PT_YIELDED; \\
\\
while(0)
PT_YIELD_UNTIL(pt, cond); // 宏语句展开后的实际代码如下
do \\
PT_YIELD_FLAG = 0; \\
(pt)->lc = __LINE__; case __LINE__: ; \\
if((PT_YIELD_FLAG == 0) || !(cond)) \\
return PT_YIELDED; \\
\\
while(0);
PT_END(pt); // 宏语句展开后的实际代码如下
; PT_YIELD_FLAG = 0; (pt)->lc = 0; return PT_ENDED; ;
Contiki process
// contiki OS process interface
// path: contiki\\core\\sys\\process.c
// A process in Contiki consists of a single pt "protothread".
/**
* Start a process.
*
* \\param p A pointer to a process structure.
*
* \\param data An argument pointer that can be passed to the new
* process
*
*/
CCIF void process_start(struct process *p, process_data_t data);
/**
* Post an asynchronous event.
*
* This function posts an asynchronous event to one or more
* processes. The handing of the event is deferred until the target
* process is scheduled by the kernel. An event can be broadcast to
* all processes, in which case all processes in the system will be
* scheduled to handle the event.
*
* \\param ev The event to be posted.
*
* \\param data The auxiliary data to be sent with the event
*
* \\param p The process to which the event should be posted, or
* PROCESS_BROADCAST if the event should be posted to all processes.
*
* \\retval PROCESS_ERR_OK The event could be posted.
*
* \\retval PROCESS_ERR_FULL The event queue was full and the event could
* not be posted.
*/
CCIF int process_post(struct process *p, process_event_t ev, process_data_t data);
/**
* Request a process to be polled.
*
* This function typically is called from an interrupt handler to
* cause a process to be polled.
*
* \\param p A pointer to the process' process structure.
*/
CCIF void process_poll(struct process *p);
/**
* Run the system once - call poll handlers and process one event.
*
* This function should be called repeatedly from the main() program
* to actually run the Contiki system. It calls the necessary poll
* handlers, and processes one event. The function returns the number
* of events that are waiting in the event queue so that the caller
* may choose to put the CPU to sleep when there are no pending
* events.
*
* \\return The number of events that are currently waiting in the
* event queue.
*/
int process_run(void);
/**
* \\brief Cause a process to exit
* \\param p The process that is to be exited
*
* This function causes a process to exit. The process can
* either be the currently executing process, or another
* process that is currently running.
*
* \\sa PROCESS_CURRENT()
*/
CCIF void process_exit(struct process *p);
Protothread 使用例子
// example
/**
* Send data.
* PT_THREAD这个宏只是定义了一个带返回char型的函数
* This macro sends data over a protosocket. The protosocket protothread blocks
* until all data has been sent and is known to have been received by
* the remote end of the TCP connection.
*
* \\param psock (struct psock *) A pointer to the protosocket over which
* data is to be sent.
*
* \\param data (uint8_t *) A pointer to the data that is to be sent.
*
* \\param datalen (unsigned int) The length of the data that is to be
* sent.
*
* \\hideinitializer
*/
PT_THREAD(psock_send(CC_REGISTER_ARG struct psock *s, const uint8_t *buf, unsigned int len))
PT_BEGIN(&s->psockpt);
/* If there is no data to send, we exit immediately. */
if(len == 0)
PT_EXIT(&s->psockpt);
/* Save the length of and a pointer to the data that is to be sent. */
s->sendptr = buf;
s->sendlen = len;
s->state = STATE_NONE;
/* We loop here until all data is sent. The s->sendlen variable is
updated by the data_sent() function. */
while(s->sendlen > 0)
/*
* The protothread will wait here until all data has been
* acknowledged and sent (data_is_acked_and_send() returns 1).
*/
PT_WAIT_UNTIL(&s->psockpt, data_is_sent_and_acked(s));
s->state = STATE_NONE;
PT_END(&s->psockpt);
static int run_trickle(struct trickle_conn *c)
clock_time_t interval;
PT_BEGIN(&c->pt);
while(1)
interval = c->interval << c->interval_scaling;
set_timer(c, &c->interval_timer, interval);
set_timer(c, &c->t, interval / 2 + (random_rand() % (interval / 2)));
c->duplicates = 0;
PT_YIELD(&c->pt); /* Wait until listen timeout */
if(c->duplicates < DUPLICATE_THRESHOLD)
send(c);
PT_YIELD(&c->pt); /* Wait until interval timer expired. */
if(c->interval_scaling < INTERVAL_MAX)
c->interval_scaling++;
PT_END(&c->pt);
/// timer_set将当前的tick值记录起来, PT_WAIT_UNTIL会检测timer_expired是否超时,
/// 超时即可继续执行下面的程序, 这样就可以实现非阻塞延时.
static int timer_expired(struct timer *t)
// clock_time()会获取当前的tick值, 如果当前值-开始值超过了延时的tick数, 那么就是超时了
return (int)(clock_time() - t->start) >= (int)t->interval; // 思考:溢出会如何呢?
struct timer input_timer;
PT_THREAD(input_thread(struct pt *pt))
PT_BEGIN(pt);
timer_set(&input_timer, 1000); // 延时1000个tick
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
timer_set(&input_timer, 100); // 延时100个tick
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
timer_set(&input_timer, 300);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
timer_set(&input_timer, 2000);
PT_WAIT_UNTIL(pt, timer_expired(&input_timer));
PT_END(pt);
参考:
Protothread入门;
状态机——protothreads;
Contiki 非阻塞延时的原理
Contiki IPC-邮箱通信;
以上是关于协程Protothread的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 协程协程取消 ② ( CPU 密集型协程任务取消 | 使用 isActive 判定协程状态 | 使用 ensureActive 函数取消协程 | 使用 yield 函数取消协程 )
Kotlin 协程协程取消 ② ( CPU 密集型协程任务取消 | 使用 isActive 判定协程状态 | 使用 ensureActive 函数取消协程 | 使用 yield 函数取消协程 )
Kotlin 协程协程取消 ① ( 协程作用域取消 | 协程作用域子协程取消 | 通过抛出异常取消协程 | Job#cancel 函数 | 自定义异常取消协程 )
Kotlin 协程协程取消 ① ( 协程作用域取消 | 协程作用域子协程取消 | 通过抛出异常取消协程 | Job#cancel 函数 | 自定义异常取消协程 )