协程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的主要内容,如果未能解决你的问题,请参考以下文章

contiki系统分析五:算法库

Kotlin 协程协程取消 ② ( CPU 密集型协程任务取消 | 使用 isActive 判定协程状态 | 使用 ensureActive 函数取消协程 | 使用 yield 函数取消协程 )

Kotlin 协程协程取消 ② ( CPU 密集型协程任务取消 | 使用 isActive 判定协程状态 | 使用 ensureActive 函数取消协程 | 使用 yield 函数取消协程 )

Kotlin 协程协程取消 ① ( 协程作用域取消 | 协程作用域子协程取消 | 通过抛出异常取消协程 | Job#cancel 函数 | 自定义异常取消协程 )

Kotlin 协程协程取消 ① ( 协程作用域取消 | 协程作用域子协程取消 | 通过抛出异常取消协程 | Job#cancel 函数 | 自定义异常取消协程 )

Python爬虫之协程,异步协程和多任务异步协程