jishanlaike 发表于 2012-2-28 11:29:45

问个protothread问题

#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
                   PT_INIT(pt); return PT_ENDED; }

#define PT_WAIT_UNTIL(pt, condition)         \
do { \
    LC_SET((pt)->lc); \
    if(!(condition)) { \
      return PT_WAITING; \
    } \
} while(0)

---------------------------------------以上是源码
我想提2个问题:
1.PT_YIELD_FLAG = 0; 这句好像没有任何用处,它是局部变量,线程退出后自动销毁,干么给他赋值0,再退出销毁呢?
2.第二个宏定义中do{}while(0)这种结构貌似可以不要,作者为什么这样写呢?

uc-zigbee 发表于 2012-2-28 11:41:08

1.PT_YIELD_FLAG = 0; 这句好像没有任何用处,它是局部变量,线程退出后自动销毁,干么给他赋值0,再退出销毁呢?   
---------------------------------------------------------------------------------
PT_YIELD_FLAG貌似作为thread等待子thread用的。


2.第二个宏定义中do{}while(0)这种结构貌似可以不要,作者为什么这样写呢?
do{}while(0)能把多个语句合成一个语句,并且还必须需要后面的;

theophilus 发表于 2012-2-28 12:25:26

1.
PT_YIELD_FLAG是局部的(初始化为1),这个变量在调用PT_YIELD*()时会被置0,然后将lc指向下一行,最后由这句
if(PT_YIELD_FLAG == 0) { return 1; } // --- (1)
来结束。

这样,当下次恢复的时候,PT_YIELD_FLAG首先被初始化为1,然后跳到(1)行
由于不满足条件,使之可以继续执行。


2.
假设某人这样写:
if (xxx)
   PT_WAIT_UNTIL(xxx);

不加do { }while(0), 你看看展开后会造成什么后果

jishanlaike 发表于 2012-2-28 14:25:20

这样,当下次恢复的时候,PT_YIELD_FLAG首先被初始化为1,然后跳到(1)行
由于不满足条件,使之可以继续执行。

-----------------------------------------------------------------------
下次重新恢复的时候,因为PT_BEGIN每次都执行,实际上重新定义了一个局部变量PT_YIELD_FLAG,PT_END退出时,已经吧PT_YIELD_FLAG这个栈上的变量销毁了,下次执行PT_BEGIN时,PT_YIELD_FLAG在栈上的位置可能已经发生了变化,所以二者名字相同,其实不是一个变量,它是局部变量,线程退出后自动销毁,干么给他赋值0,再退出销毁呢?


do { }while(0), 那个,theophilus 说的很有道理,那么可以支架{},不加do和while(0)了。。

theophilus 发表于 2012-2-28 15:01:11

理论上确实是不需要的,PT_YIELD_FLAG只是一个HACK.
你需要仔细看看宏定义,另外,如果你用gcc的话,可以用 gcc 的 -E -P 来输出展开宏之后的代码,方便分析

比如下例:
#include "pt.h"

static PT_THREAD(blah(struct pt *pt))
{
    static int delay;

    PT_BEGIN(pt);

    for(delay = 1234; delay > 0; delay -= 1) {
      // do something
      // ...
      // NOTE: Yield here
      PT_YIELD(pt);
    }

    PT_END(pt);
}

展开后:

typedef unsigned short lc_t;
struct pt {
lc_t lc;
};
static char blah(struct pt *pt)
{
    static int delay;
    { char PT_YIELD_FLAG = 1; switch((pt)->lc) { case 0:; // 这里先让PT_YIELD_FLAG = 1, 再执行跳转
    for(delay = 1234; delay > 0; delay -= 1) {



      do { PT_YIELD_FLAG = 0; (pt)->lc = 13; case 13:; if(PT_YIELD_FLAG == 0) { return 1; } } while(0); // 注意这一行
    }

    }; PT_YIELD_FLAG = 0; (pt)->lc = 0;; return 3; };
}


主要注意红色这一行,"(pt)->lc = 13" 和 "case 13:",你要分析一下两次执行blah()会发生什么情况。
其实有办法让 (pt)->lc 等于跳过 "return 1" 下面一句的行标,PT_YIELD_FLAG 就完全不需要了,只是宏定义起来不会优雅。

theophilus 发表于 2012-2-28 15:39:33

回复【3楼】jishanlaike阿弱
这样,当下次恢复的时候,pt_yield_flag首先被初始化为1,然后跳到(1)行
由于不满足条件,使之可以继续执行。
-----------------------------------------------------------------------
下次重新恢复的时候,因为pt_begin每次都执行,实际上重新定义了一个局部变量pt_yield_flag,pt_end退出时,已经吧pt_yield_flag这个栈上的变量销毁了,下次执行pt_begin时,pt_yield_flag在栈上的位置可能已经发生了变化,所以二者名字相同,其实不是一个变量,它是局部变量,线程退出后自动销毁,干么给他赋值0,再退出销毁呢?
do { }while(0), 那个,theophilus 说的很有道理,那么可以支架{},不加do和while(0)了。。

-----------------------------------------------------------------------

不要直接用 {}
比如
#define XXX() { /* ... */ }

if (xxx)
   XXX();
else
   one();

展开后, '}'后面, else前面会多一个分号,照样出错。

jishanlaike 发表于 2012-2-29 11:47:41

theophilus:非常感谢!

static char blah(struct pt *pt)
{
    static int delay;
    { char PT_YIELD_FLAG = 1; switch((pt)->lc) { case 0:;
    for(delay = 1234; delay > 0; delay -= 1) {



      do { PT_YIELD_FLAG = 0; (pt)->lc = 13; case 13:; if(PT_YIELD_FLAG == 0) { return 1; } } while(0);
    }

    }; PT_YIELD_FLAG = 0; (pt)->lc = 0;; return 3; };   // 我的意思是:这行的PT_YIELD_FLAG = 0;好像没有任何用处,可以省略?
}

theophilus 发表于 2012-2-29 15:21:12

这里是没有用。
页: [1]
查看完整版本: 问个protothread问题