前 的个人资料miss your eye照片日志列表 工具 帮助

日志


9月4日

snow leopard升级后的一些问题

* iphone sdk for snow leopard强力减肥,从2g+变为400M+,还真不能高兴的太早,虽说下载没那么痛苦了,可这个sdk里除了iphone相关的就没别的了,如果升级时没安装snow leopard带的xcode,/usr/bin下面可没有gcc给你用
*python升级到了2.6.1,就差那么点儿也不给2.6.2
*gcc们默认编译为x64 obj,假如忘了-m32参数,link的时候够你烦的,同理,n多opensource要重编译⋯⋯
*某些自动运行的app升级后不见了?也不一定,找到运行一下,会再次提示你这玩艺来自不安全的web⋯⋯
*到底用不用64位模式,这个问题经常在睡梦中不断的折磨着我

- 9月5日
*好吧,系统送的python2.6,自己是universal binary,所以也只能加载universal模块
*universal在snow leopard上大多时候是指32_64 fat,也就是x86 + x86_64组合,ppc32,64可以编译(?),但自己已然不支持
*所以自己编译的python模块也得是universal
*有时这比较难办,比如boost,单独编译i386或x64都没问题,universal就败了,等更新吧
*google v8只有ia32 asm和arm asm,目前不能universal有x64,没有尝试过build universal
*macports正在努力升级自己的python26成为universal,现在upgrade会失败,幸好,还是留着10.5的32bit python26吧,脚本语言搞的这么平台依赖怎么行
*非c语言的module很安全
*我做的那个亲爱的python javascript wrapper module for python 3.1算是毁了,没人爱用python3,我自己都回来找2.6了



8月13日

小强有多强

早上起来,打开冰箱拿酸奶,听见烤箱里希希邃邃的响,疑似小强潜入,抬头一看,果不其然,有只小强正在里面寻路。顺手拧了一下开关,就洗漱去了,出来一看烤箱里面通红一片,心中不禁一阵狰狞,于是关了开关前去验尸,结果一拉开门,没闻到熟悉的蛋白质焦臭,疑惑,正待低头细看,忽见一只小强飞速从烤箱中逃窜出来,震惊,等到反应过来,小强已经不见踪影……
7月14日

大爱~ 勇者斗恶龙9

买了nds之后也就好好玩了雷顿教授,剩下的游戏都半途而废了。不过突然有了dq 9,总算又对ndsl爱不释手了,还可以激动的高呼:我日语没白学……

杀メタル史莱姆用メタル斬完全就是个想当然的误解,一次1-2的伤害,基本不解决问题,好的办法就是枪技一闪,击中就ok,金属3合1有12k的exp,爽的不得了。

史莱姆一套,hoho

 

红黑树c++代码

 
几乎被这玩意弄成脑死亡。
注意:最好把class T换成typename T,自己实现特化Compare时需要单加一个.h,我对模板一知半解,如果特化Compare放在T类型定义的.h里,编译就出错,error redifinition
 
enum
{
 RB_Red,
 RB_Black,
};
template <class T>
class RBTreeNode
{
public:
 RBTreeNode* _link[2];
 T* _data;
 RBTreeNode* _parent;
 unsigned char _color;
};
template <typename T>
int Compare(T* t1, T* t2)
{
 return t1 == t2 ? 0 : (t1 < t2 ? -1 : 1);
};
template <class T>
class RBTree
{
public:
 RBTreeNode<T>* search(T* it);
 RBTreeNode<T>* insert(T* it);
 RBTreeNode<T>* probe(T* item);
 T* remove(T* item);
 RBTreeNode<T>* rebalance(RBTreeNode<T>* n);
private:
 RBTreeNode<T>* _root;
 int _count;
 int _gen;
};
template<class T> inline RBTreeNode<T> *RBTree<T>::search(T* it)
{
 int d = 0;
 RBTreeNode<T> *p = NULL, *n = NULL, *q = NULL;
 // search
 for (q = NULL, p = _root; p != NULL; q = p, p = p->_link[d])
 {
  int cmp = Compare<T>(it, p->_data);
  if(cmp == 0)
   return p;
  d = cmp > 0;
 }
 return NULL;
}
template<class T> inline RBTreeNode<T> *RBTree<T>::probe(T* it)
{
 int d = 0;
 RBTreeNode<T> *p = NULL, *n = NULL, *q = NULL;
 // search
 for (q = NULL, p = _root; p != NULL; q = p, p = p->_link[d])
 {
  int cmp = Compare<T>(it, p->_data);
  if(cmp == 0)
   return p;
  d = cmp > 0;
 }
 // insert
 n = new RBTreeNode<T>;
 if(!n)
  return NULL;
 _count++;
 n->_link[0] = n->_link[1] = NULL;
 n->_parent = q;
 n->_data = it;
 if(q != NULL)
 {
  q->_link[d] = n;
 }
 else
  _root = n;
 n->_color = RB_Red;
 // rebalance
 q = n;
 while(true)
 {
  RBTreeNode<T> *f, *g;
  if(!q->_parent)
   break;
  f = q->_parent;
  if(!f->_parent)
   break;
  g = f->_parent;
  if(g->_link[0] == f)
  {
   // rebalance left
   RBTreeNode<T>* h = g->_link[1];
   if(h != NULL && h->_color == RB_Red)
   {
    // q has a red uncle 
    h->_color = f->_color = RB_Black;
    g->_color = RB_Red;
    q = g;
   }
   else
   {
    RBTreeNode<T>* i = g->_parent;
    if(i == NULL)
     i = _root;
    if(f->_link[1] == q)
    {
     // q is right son of f
     // exchange q & f, let f become q's left son, then 
     f->_link[1] = q->_link[0];
     q->_link[0] = f;
     g->_link[0] = q;
     f->_parent = q;
     if(f->_link[1] != NULL)
      f->_link[1]->_parent = f;
     f = q;
    }
    // q is left son of f 
    // rotate right at g
    g->_color = RB_Red;
    f->_color = RB_Black;
    g->_link[0] = f->_link[1];
    f->_link[1] = g;
    i->_link[i->_link[1] == g] = f;
    f->_parent = g->_parent;
    g->_parent = f;
    if(g->_link[0] != NULL)
     g->_link[0]->_parent = g;
    break;
   }
  }
  else
  {
   // right 
   RBTreeNode<T>* h = g->_link[0];
   if(h != NULL && h->_color = RB_Red)
   {
    // case 1
    f->_color = h->_color = RB_Black;
    g->_color = RB_Red;
    q = g;
   }
   else
   {
    RBTreeNode<T>* i = g->_parent;
    if(i == NULL)
     i = _root;
    if(f->_link[0] == q)
    {
     // case 3, transform to case 2
     f->_link[0] = q->_link[1];
     q->_link[1] = f;
     g->_link[1] = q;
     f->_parent = q;
     if(f->_link[0] != NULL)
      f->_link[0]->_parent = f;
     f = q;
    }
    // case 2
    g->_color = RB_Red;
    f->_color = RB_Black;
    g->_link[1] = f->_link[0];
    f->_link[0] = g;
    i->_link[i->_link[0] == g] = f;
    f->_parent = g->_parent;
    g->_parent = f;
    if(g->_link[1] != NULL)
     g->_link[1]->_parent = g;
    break;
   }
  }
 }
 _root->_color = RB_Black;
 return n;
}
// Holy~ brain killer ...
template<class T> inline T *RBTree<T>::remove(T *item)
{
 RBTreeNode<T> *p, *q, *f;
 T* it = NULL;
 int d;
 // find target
 if(!_root)
  return NULL;
 for (q = NULL, p = _root; p != NULL; q = p, p = p->_link[d])
 {
  int cmp = Compare<T>(it, p->_data);
  if(cmp == 0)
   break;
  d = cmp > 0;
 }
 if(!p)
  return NULL;
 it = p->_data;
 q = p->_parent;
 if(q == NULL)
 {
  q = (RBTreeNode<T>*)&_root; // c pointer trick: q->_link[0] = root, _link[1] is _count
  d = 0;
 }
 // delete target
 if(p->_link[1] == NULL)
 {
  // no right son.
  q->_link[d] = p->_link[0];
  if(q->_link[d] != NULL)
   q->_link[d]->_parent = p->_parent;
  f = q;
 }
 else
 {
  unsigned char c;
  RBTreeNode<T>* r = p->_link[1];
  if(r->_link[0] == NULL)
  {
   // right son doesn't have a left son 
   r->_link[0] = p->_link[0];
   q->_link[d] = r;
   r->_parent = p->_parent;
   if(r->_link[0] != NULL)
    r->_link[0]->_parent = r;
   c = p->_color;
   p->_color = r->_color;
   r->_color = c;
   f = r;
   d = 1;
  }
  else
  {
   // right son has a left son ...
   RBTreeNode<T>* s = r->_link[0];
   while(s->_link[0] != NULL)
   {
    s = s->_link[0]; // find the successor
   }
   r = s->_parent;
   r->_link[0] = s->_link[1];
   s->_link[0] = p->_link[0];
   s->_link[1] = p->_link[1];
   q->_link[d] = s;
   if(s->_link[0] != NULL)
   {
    s->_link[0]->_parent = s;
   }
   s->_link[1]->_parent = s;
   s->_parent = p->_parent;
   if(r->_link[0] != NULL)
    r->_link[0]->_parent = r;
   c = p->_color;
   p->_color = s->_color;
   s->_color = c;
   f = r;
   d = 0;
  }
 }
 // rebalance
 if(p->_color = RB_Black)
 {
  while(true)
  {
   RBTreeNode<T> *x, *g, *y;
   x = f->_link[d];
   if(x != NULL && x->_color == RB_Red)
   {
    x->_color = RB_Black;
    break;
   }
   if(f == (RBTreeNode<T>*) &_root)
    break;
   g = f->_parent;
   if(g == NULL)
    g = (RBTreeNode<T>*) &_root;
   if(d == 0)
   {
    // left side rebalance
    RBTreeNode<T>* w = f->_link[1];
    if(w->_color == RB_Red)
    {
     // w is black
     w->_color = RB_Black;
     f->_color = RB_Red;
     f->_link[1] = w->_link[0];
     w->_link[0] = f;
     g->_link[g->_link[0] != f] = w;
     w->_parent = f->_parent;
     f->_parent = w;
     g = w;
     w->_parent = f;
    }
    if( (w->_link[0] == NULL || w->_link[0]->_color == RB_Black)
      && (w->_link[1] == NULL || w->_link[1]->_color == RB_Black))
    {
     // left side rebalance
     w->_color = RB_Red;
    }
    else
    {
     if(w->_link[1] == NULL
       || w->_link[1]->_color == RB_Black)
     {
      // transform left-side rebalancing
      RBTreeNode<T>* y = w->_link[0];
      y->_color = RB_Black;
      w->_color = RB_Red;
      w->_link[0] = y->_link[1];
      y->_link[1] = w;
      if(w->_link[0] != NULL)
       w->_link[0]->_parent = w;
      w = f->_link[1] = y;
      w->_link[1]->_parent = w;
     }

     w->_color = f->_color;
     f->_color = RB_Black;
     w->_link[1]->_color = RB_Black;
     f->_link[1] = w->_link[0];
     w->_link[0] = f;
     g->_link[g->_link[0] != f] = w;
     w->_parent = f->_parent;
     f->_parent = w;
     if(f->_link[1] != NULL)
      f->_link[1]->_parent = f;
     break;
    }
   }
   else
   {
    // right side rebalance
    RBTreeNode<T>* w = f->_link[0];
    if(w->_color == RB_Red)
    {
     w->_color = RB_Black;
     f->_color = RB_Red;
     f->_link[0] = w->_link[1];
     w->_link[1] = f;
     g->_link[g->_link[0] != f] = w;
     w->_parent = f->_parent;
     f->_parent = w;
     g = w;
     w = f->_link[0];
     w->_parent = f;
    }
    if( (w->_link[0] == NULL || w->_link[0]->_color = RB_Black)
      && (w->_link[1] == NULL || w->_link[1]->_color == RB_Black))
    {
     w->_color = RB_Red;
    }
    else
    {
     if(w->_link[0] == NULL || w->_link[0]->_color = RB_Black )
     {
      RBTreeNode<T>* y = w->_link[1];
      y->_color = RB_Black;
      w->_color = RB_Red;
      w->_link[1] = y->_link[0];
      y->_link[0] = w;
      if(w->_link[1] != NULL)
       w->_link[1]->_parent = w;
      w = f->_link[0] = y;
      w->_link[0]->_parent = w;
     }
     w->_color = f->_color;
     f->_color = RB_Black;
     w->_link[0]->_color = RB_Black;
     f->_link[0] = w->_link[1];
     w->_link[1] = f;
     g->_link[g->_link[0] != f] = w;
     w->_parent = f->_parent;
     f->_parent = w;
     if(f->_link[0] != NULL)
      f->_link[0]->_parent = f;
     break;
    }
   }
   y = f;
   f = f->_parent;
   if(f == NULL)
    f = (RBTreeNode<T>*) &_root;
   d = f->_link[0] != y;
  }
 }
 //
 return p ? p->_data : NULL;
}
5月11日

用double CAS实现lock-free队列

参考 Optimised Lock-Free FIFO Queue

实现了一个lock-free的fifo。在ia32上,可以用cmpxchg8b来模拟double cas,只是操作数中,2个需要更新的值必须是连续的内存地址。

定义宏如下:
#define CAS2(ret, mem, old1, old2, new1, new2) \

do {\
asm volatile( \
".align 4 \n" \
"movl $0, %0 \n" \
"LOCK cmpxchg8b (%%esi)\n" \
"setz %0 \n" \
: "=m" (ret) \
: "S" (mem), "a" (old1), "d" (old2), "b" (new1), "c" (new2) \
: "memory", "cc" \
); \
} while(0);



#define DEAD_NODE(q) \
&((q)->dead)

#define CAS(ret, mem, old, new) \
do {\
asm volatile( \
".align 4 \n" \
"movl $0, %0 \n" \
"LOCK cmpxchgl %%edx, (%%esi) \n" \
"setz %0 \n" \
: "=m" (ret) \
: "d" (new), "S" (mem), "a" (old) \
: "memory", "cc" \
); \
} while(0);


struct 
{
     node* head;
     unsigned long tag1;
     node* tail;
     unsigned long tag2;
     node dead;
} queue;


struct
{
     node* next;
     void* data;
} node;
---------------------------------------------------------------------------------------------------------

init


void init_queue(queue* q)
{
     memset(q, 0, sizeof(queue));
     q->head = q->tail = &q->dead;
}


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

pop


node* pop(queue* q)
{
     node* n = NULL;


     while(1)
     {
          int ret;
          unsigned long tag1 = q->tag1;
          node* head = q->head;
          unsigned long tag2 = q->tag2;
          node* next = head->next;
          if ( tag1 == q->tag1 )
          {
               if(head == q->tail)
               {
                    if(next == NULL)
                         return NULL;
                    CAS2(ret, &q->tail, head, tag2, next, tag2 + 1);

               }
               else if(next != NULL)
               {
                    CAS2(ret, &q->head, head, tag1, next, tag1 + 1);
                    if(ret)
                    {
                         n = head;
                         break;
                    }
               }
          }
     }
     
     if(n == DEAD_NODE(q))
     {
          push(q, n);
          n = pop(q);
     }


     return n;
}
---------------------------------------------------------------------------------------------------------

push


void push(queue* q, node* n)
{
     n->next = NULL;
     while(1)
     {
          int ret = 0;
          unsigned long tag2 = q->tag2;
          node* tail = q->tail;
          CAS(ret, &tail->next, NULL, n);
          if(ret)
               break;
          CAS2(ret, &q->tail, tail, tag2, tail->next, tag2 + 1);
     }
     CAS2(ret, &q->tail, tail, tag2, n, tag2 + 1);
}
3月25日

爱表现的阿姨

新来了一个阿姨,总是在正干活的时候过来做清扫,抹桌子,拖地,换垃圾袋,一定要让你看在眼里不说,还一定要让你亲身感受到,这不,正敲键盘写代码,一只手抓着一块抹布过来,在桌子上围着你平放的双臂画出一幅拓片,于是你不好意思的收回双手,停下工作,然后抹布就很顺畅的在你的键盘和屏幕上游走一遍,让人哭笑不得。

这还不算完,好容易坐定,一条拖把又悄然无息的伸到脚下,轻轻而又坚决的触碰着你的鞋子,你一定要满含歉意的收回双脚,打断思路,退后三尺,在阿姨豁达宽容的话语中(没事,不用动,你忙你的),频频点头称是。

早上临近11点,迟到的我终于赶到公司,阿姨已经打扫完毕,心中窃喜,赶忙坐下,拿出电脑,顺手摸把桌子……一手黑灰,我操。
2月24日

笔记本出院了

送修一周,拿回来了,换了左侧风扇,不再满腹委屈的怪叫,也不发烧抗议了。
于是难得有了2三天没电脑用的日子,本以为很难熬,却也很平静的度过了。
顺便给满是胡须泥垢的厨房地板擦净了脸,为堪比越冬蔬菜大棚的冷藏室降了温,这个容我藏身5年多的小地方,默默忍受着我对生活毫无关爱的所作所为,宽容的就像那些善待我的亲人朋友们一样。
最后要躺下检讨检讨,我还是热爱生命的,总想象着周围的一切都是活着具有灵性的,一会我闭眼了要憧憬明天能打出个塔格奥腰带或是乔丹之石,不想背可怜的笔记本上班了地铁那安检公司的小p孩们每次都不放过牠,还有老猫那个会倒翻跟头的青蛙很帅,我也想练那招……
12月9日

neocd emu for iphone

68k core works!
issue & todo:
* low performance
* Z80 crashed so no sound
* cdda/mp3 not supported so no music
* input subsystem accepts nothing ... haha ~ yup I mean it can do nothing now.
here is a snapshot on device
 
and another on simulator
 
11月3日

pcre正则表达式库的使用

对 于约定格式的字符串处理,假如需要对应的格式较多,而且随着需求变化成线性增长,那么最好从一开始就使用正则表达式,否则会演变成自虐行为。在 c/c++里可使用的库并不多,posix正则库在*nix上基本已经算是内建了,但是在mac上看man page,提及仍然是alpha阶段,不但性能不高,而且有一些bug,似乎从2004年开始就没有什么改进,而linux上没有这样的警告,不过在我草 草看了一遍pcre(perl compatible regular expressions)的man page之后,还是决定麻烦一下自己,给工程增加一个第三方依赖。

pcre吸引我的首要的好处就是支持utf8,我在工程里一直偷懒回避字符集问题,对于所有的字符串都视为utf8,工程所用到的xml,数据库字符集也 都设置为utf8,偷懒的结果就是许多分析串的代码都成了心病,内容无关的场合按字节处理也许不是问题,内容相关的场合,按字符处理就大大的麻烦,用支持 utf8的正则库分解字串正好可以消除一些隐患。

pcre的安装很简单,发行版linux上在线安装就可以,在fedora 8上,
# yum install pcre pcre-devel
即可。

mac上我没用port,直接下载的源代码编译安装了。configure时注意utf8支持默认是关闭的。

最基本的正则匹配过程分为两步,模式分析和匹配应用。
compile用于模式分析,exec用于匹配串。

举例:现有类似$MOVE#username#x,y#msg$的消息,其中x和y是整数,msg包含任意字符,那么模式为(^\$MOVE)#(.*)#([0-9]*),([0-9]*)#(.*)\$$,差不多了,来段代码看看


    pcre *re;
    const char *error;
    char *pattern;
    char *subject;
    unsigned char *name_table;
    int erroffset;
    int find_all;
    int namecount;
    int name_entry_size;
    int ovector[30];
    int subject_length;
    int rc, i;

    pattern = (char*) "(^\\$MOVE)#(.*)#([0-9]*),([0-9]*)#(.*)\\$$";
    subject = (char*) "$MOVE#robot#100,100#haha\ntest$";
   
    subject_length = (int) strlen(subject);

    re = pcre_compile(pattern,
        PCRE_UTF8,
        &error,
        &erroffset,
        NULL);

    if (re == NULL)
    {
        ...
    }

    rc = pcre_exec(re, NULL, subject, subject_length, 0, 0, ovector,     30);
    if (rc < PCRE_ERROR_NOMATCH)
    {
        printf("No match\n");
    }

    /* Show substrings stored in the output vector by number. Obviously, in a real
     application you might want to do things other than print them. */

    for (i = 0; rc == 0 && i < 10; i++)
    {
        char *substring_start = subject + ovector[2 * i];
        int substring_length = ovector[2 * i + 1] - ovector[2 * i];
        printf("%2d: %.*s\n", i, substring_length, substring_start);
    }

运行结果:
No Match

哈哈,我不是逗你玩,生活需要调剂嘛。目标串含有一个换行符是个问题,默认来说匹配是执行到行尾,有两个选择,一个是给pcre_compile的第二个 参数合并上PCRE_DOTALL,另一个是在模式前面加上选项:'(?s)(^\$MOVE)#(.*)#(\d+),(\d+)#(.*)\$$'

(?s)就是强制匹配到字串结尾。一般默认是多行匹配(?m),行分隔符在不同的系统上有着倔强的不同,windows是CRLF,苹果是CR,linux是LF,根据不同的系统,只好分别设置选项。
(*CR)对应PCRE_NEWLINE_CR
(*CRLF) - PCRE_NEWLINE_CRLF
(*LF) - PCRE_NEWLINE_LF
(*ANYCRLF) - PCRE_NEWLINE_ANYCRLF
或者(*ANY) - PCRE_NEWLINE_ANY

我个人还是倾向于把选项写到模式中,我是把模式都放在xml中的,这样自然比写死在代码里来得灵活。要注意(*)这样的选项需要出现在模式的最前面,而(?ms)随便放了,这样都行:'(^\$MOVE)#(.*)#(\d+),(\d+)#(?m)(.*)\$$'


我把[0-9]*换成\d+了,意思基本相同,不同的在于*和+,前者允许零匹配,后者至少得有一个。

加了(?s)了,再试……

 0: $MOVE#robot#100,100#haha
test$
 1: $MOVE
 2: robot
 3: 100
 4: 100
 5: haha
test


好了

ovector中是每个字串的起始位置和结束位置,最前面那一对是匹配到的全长,假如输入串完全满足模式,那么第一对就是输入串的开始与结尾了。

试试吧,程序员都会爱上正则表达式这玩意的。
11月2日

南瓜头

水果刀不好用……
11月1日

c/c++结合lua制造NPC

lua是大虾推荐的脚本引擎,高效小巧,很容易和c语言交互。

我的工程起初的设计比较粗陋,并没有考虑太多扩展性,在面对增加task子系统的需求时,对于到底是保证时间还是更改结构颇为犹豫,最后还是狠下心决定编写一个扩展task子系统,代价就是花了3个通宵。 当时只是粗略的看了一下lua,边翻手册边google连抄带试的做,很多lua特性,比如关于coroutine(lua里一个有趣的伪并行机制),以及结合os thread的方式,完全没能理解,于是采取了一个低效但安全的方式回避了问题。

由于几乎全部的用户数据、状态都在server里维护,我将通信模块处理生成的事件中的一部分再次转译为一组消息派发给另外一套处理系统,这套处理系统可接受消息处理接口的注册请求,将这些接口实例组织成一个响应链,传递并处理消息,而可选的,这些实例可以实现为一个队列,转为异步处理消息。

task子系统实现了同步消息处理接口,每个task编写一个对应的lua脚本,其中除了task的各种定义数据之外,就是一组消息响应函数,根据需要实现。c则导出一些函数供lua调用,用来访问用户的状态和数据。

c通过lua_State指针与lua交互,lua_State以下简称lua状态,不考虑其实现细节,可以看作是一个栈,lua里一切都是对栈上的元素进行操作,而lua本身为了尽量减少系统依赖,并没有增加同步机制,所以多线程环境里,只能外部对lua状态加锁,或者给每一个线程创建一个单独的lua状态,不过虽然消息处理系统的线程数量固定,但线程池本身与task系统没什么关系,考虑到目前task脚本都很小,数量短期内也不会增加太多,而玩家是否拥有一个task,是在进行响应之前就判断过的,加上task中的响应函数本身是无状态的,并不参与维护任何用户数据和状态,所以我最后对于每个task消息请求,都加载了一次脚本。每次调用的时间开销小于1ms,可以接受。

实现NPC系统就不能这么偷懒了,我希望npc能够自己维护自己的状态,不需要c来记录,这样npc脚本就必须保持运行。c加载运行lua脚本之后,如果脚本保持运行状态,c线程将被阻塞,为每一个npc创建一个线程代价就太大了点儿。没办法,只能啃coroutine了,不过这次就犯不着玩通宵了。

coroutine是一种伪并行机制,它以一个函数为参数建立一个具有一般线程特性的lua状态,拥有自己的数据和栈,同时又共享全局数据,通过yield暂停,resume运行,在一个脚本里可以创建多个coroutine,不过并不存在真正的同时运行,仍然需要自己编写切换线程的逻辑。那么假如在c中可以控制coroutine,使得脚本在yield之后返回c线程,再从c线程调用resume恢复coroutine,如此反复,就可以虚拟出帧循环时间片,逐帧响应的npc就能够以投骰子的方式决定自己的行为。

非常幸运,lua具有这样的功能。

/* C code - start */
lua_getglobal(lua, "coroutine");
lua_pushstring(lua, "create");

lua_gettable(lua, -2);
lua_getglobal(lua, "thread_body");

lua_call(lua, 1, 1);

lua_State* co = lua_tothread(lua, -1);
lua_setglobal(lua, "co");
......
for(;TRUE;)
{
    while(npc)
    {
        lua_resume(npc->co, 0);
        npc = npc->next;
    }
    // wait for next frame
}

/* C code - end */

--------------------------
-- lua code - start
--------------------------

function on_frame()
    print ("Moving or Talking or Attacking ... ")
end

function thread_body()
    while 1 do
        on_frame()
        coroutine.yield()
    end
end

--------------------------
-- lua code - end
--------------------------

这样一个c线程就可以轮询数个npc了。

从lua_tothread这个函数就可以知道co这个状态是被lua定义为thread类型的,而创建一个thread不是只有coroutine.create能做,C API lua_newthread也可以,睡觉了,下回继续说吧。

8月15日

有些清净的日子

% 回北京后发觉这城市难得的清静,机场出口一路都没什么人,小小的意外。
% 天气有点闷,人也懒散,早上怎么都睡不醒,自然勤也就没怎么出。
% 不关心奥运,可内心里仍然热爱比赛们,电视开了就关不了,有点烦人。
% 活儿算是干完一期了,貌似这些天server们转的挺好,signal 11之流没来搅扰我看球的心情。
% 开始构思和整理filesystem的spec。
% 头疼,总头疼,我恨各种空调。
% 热烈庆祝cod终于回归了兄弟3人小组,不过我迷上了用rpg火箭筒一炮打出airstrike的爽快,彻底步入旁门左道。
% 中午看完谢大美女和张宁姐姐的比赛才恋恋不舍的出来上班,意外赶上寿星缺勤的生日会,大喜,狂吃8块西瓜。
% 想买10寸小笔电,编译一次代码花30分钟,期间可以吃喝拉撒。
% 屋里蟑螂社会飞速发展,完成了从种群到部落的转变,已经上我床了。
% animorphs小说还不错,简单e文,我一目3行。
% 我很臭,1个月没洗澡了,哇哈哈哈……
7月28日

歪招

需要从server检查client超时,另开一个线程定时轮询所有client从实现上讲是简单的,不过心理上是有无法逾越的障碍的,我怎么就那么烦轮询,懒人就应该等通知而不是反复刷。咨询了几位大牛,给我的建议也是设置队列,无奈只好奋斗了一下,又把client结构纵横交错了一条队列出来,外加一个邪恶的监视线程,算是完事了。

不过心里总觉得疙瘩,躺在床上辗转不能安眠,我不要轮询我不要轮询…… 突然就灵光一现,假如我在client活动的时候更新一个定时器,如果定时器触发就认为是超时了岂不就成了事件驱动了么?不过client不能太多,不然就要命了。

7月23日

reader-writer-lock

reader-> lock -> readers < max_readers ? reader++ : blocking ...
reader-> unlock -> readers--
 
writer -> lock -> exclusive_lock(); for(i=0;i<max_readers;i++,readers++) ;
writer -> unlock -> readers = 0; exclusive_unlock();
7月21日

布丁8岁了

人活着挺不容易,还在懵懂的时候就开始匆忙的选择,一步步忙乱的下一盘不知什么时候能终结,却必定下不完的棋。我时常不自觉的回头看,不是因为我喜欢怀旧,而是前面的路看不那么清楚。但回头看到的悲伤们让我不敢注目,虽然视线只是一闪而过,却仍然让我心跳+5,久久不能平复。而那些喜悦们,也很快就变成失落的怀念,分别之后的过去与现在,就如同自己的未来一样,看不清楚,无从得知。
 
8年前,8月中,我在西安,挑了个周末,去了宠物市场。我都不记得那是群众路还是劳动路,我真是个人类文明的耻辱,每个呆过的城市里的道路公交,我都完全无知,虽然方向感很准确,可以总是保证我徒步数小时后顺利到达,但我不得不说,那很累。不记得宠物市场什么样了,只记得摊主问我要大猫小猫,然后让我看4只满月的小猫,2个虎斑,2个小白,我犹豫了3秒钟,选了个虎斑,35块。
 
回去的时候打了车,坐后排,小猫还没我手掌长,叫声奶声奶气的,甚至还不怎么会走路,后腿总是撇开着,走起来摇摇摆摆,看着笨笨的。我把它托在掌心,让它看窗外的街景,而它似乎还意识不到速度,傻乎乎的就想走出去,一点也不知道害怕,对周围环境的变化也毫无知觉,我下车,上楼,带它进屋,它都只顾着走来走去,一点没管脚下到底是我的手,还是肩膀,或是沙发……
 
我打小喜欢猫,家里也养过,不过自己都是看着老妈和老姐照顾猫,没亲手干过换猫灰这种又脏又臭的事。但是我天资还算聪颖,看过的事都基本能照猫画出猪来。太白花园对面的徐家庄市场那些小馆子们每天会扔大量的蜂窝煤灰,于是我拎了个大塑料袋下去,在市场口打转,我不大会跟人说话,实在有点不好意思开口,犹豫了半天是不是干脆去垃圾车那淘。还好有个善解人意的大妈从我渴望的眼神中看穿了我的企图,她喊我:"要煤灰?养猫?"我忙不迭的点头,她就慷慨的一挥手,"来拿,要多少都行。"我感动的稀里哗啦的,上去抖开了袋子就塞,大妈看我手笨,还递过来把火钳,唉,周到。
 
我把煤灰踩碎了铺在簸箕里,放在客厅阳台一角,记得猫好像头一次认准了在哪大小便,以后就很难改了,所以我就一路跟着小奶猫,看它什么时候有尿欲,随时准备在那一刻把它抓到阳台上去。结果一直到我晚上准备睡觉,小奶猫也没排泄,算了,我没耐性继续盯着它了,我把它放阳台上,倒了牛奶给它,趁它喝奶的时候回我那屋爬上床准备睡了。
 
刚躺下,就听见小猫奶声奶气的叫,好像很害怕,也不知怎么了。我正打算起来看,看见它跑进我屋里,一直跑到我床头,看着我使劲叫。干嘛,想上来?我可不想惯你这毛病。结果小猫自己使劲一跳,抓住了床单下摆,一步一步自己爬上来了,这倒让我很是吃惊。它爬上来之后就钻到我脖子那,把小脑袋枕到我枕头上,好像睡觉也得跟我一样姿势。有时候可能做的再多,也换不来一个人对自己这么亲近,不过那时候我肯定没意识到这些有多可贵。
 
第二天早上醒了,小猫已经在地上滚来滚去的咬我的鞋了,难道不觉得臭么。我起来洗漱,看见牛奶少了不少,半夜估计还跑出来喝奶了,喝这么多怎么不尿呢,反正也要迟到了,干脆再盯一会吧。果然,小猫突然跑到我和李凌的电脑桌中间那块报纸上,一边喵喵叫,一边就蹲下去了,我立即就反应过来是怎么回事了,一个箭步冲过去,抓着它后脖子提起来,不过已经晚了,小猫在我手上就像个浇花的洒水壶一样……
 
我把洒水壶放在簸箕上,看着它本能的去埋好,这下小猫算是安居了,而且被我识别出了性别为男性,不过还没有名字,除了我叫它蠢猫,大家还是叫它咪咪的多,小赵mm虽然给它起了名字叫布丁,可没人遵行,随着小猫一天天变得淘气顽劣,蠢猫这个称谓逐渐被淘汰了,李凌对于它的淘气又恨又爱,于是小猫成了"小混蛋"。
 
初中时的数学老师说,天底下2种人最麻烦,老幺和独生子。随着我一年年老去,也一点点领会着他没说出口的那半截话。虽说4年大学生活能在无限肮脏和杂乱里混完,让我生活能够基本自理,也让我信心满满的以为完全可以照顾好一头刚断奶的小猫。实际上小混蛋的生活虽然跟我过得多姿多彩,却绝对没被照顾好。
 
起先我坚持人吃什么猫吃什么,这是我老妈的原则。不过这原则完全没执行就放弃了。原因很简单,我的生活完全无规律,不知道什么时候有吃的,也不知道吃的是什么。以前下班晚了,直奔徐家庄烤肉摊,一盘炒面,一把烤肉,一瓶冷饮,10块钱打发了,这些吃的,且不说小混蛋拒绝吃,它吃我还怕吃坏了它。这么一来只好来人工调配,火腿肠+牛奶。这也没坚持多久,我总抢它的存粮,没办法,小混蛋,你爹我也饿啊。最后,只好买猫粮了。头一次买回来之后,我也尝了一颗,唉,不怎么好吃,好了,以后不会跟你抢了。
 
不过猫粮可不便宜,那时候我比现在还穷,就不怎么舍得给小混蛋吃,小家伙长得快,胃口也呼呼得涨,我口袋里的钱也不见多。结果就是我老人家和小混蛋随时都处于饥饿状态。当然我对于饥饿的应对方式很简单,用精神食粮填补,饿了我就打打游戏,看看书,写写程序。小混蛋也有它的方式,咬咬电线,撕撕报纸,扯扯塑料袋……
 
小猫的发泄方式自然有副作用,我倒是担心过一阵子它被电击致死,不过屋里的电线质量还算过得去,所以它的发泄引发的最大恶果就是遭李凌"毒打"。李凌同学那时候每天早8:30-晚8:30在民族软件的脊梁卖命,时不时还要借出差为由云游四海1-3个月不等,当我俩都在电脑前发愤的时候,无所事事的小猫只好走到电脑后那片各种电线纵横交错的区域,随便捏一根使劲撕咬,来填补饥饿的心灵,或是趴在键盘后面,聚精会神的看着我抽筋般敲动的手指,然后伸爪打乱我抽筋的节奏,在我手指上留下几道好奇的抓痕。
 
假如是后者,顶多被我抓住后颈提到空中,然后被抓住四肢倒悬空中,以头部重击2到3下墙壁,最后被装入正合身的塑料口袋,挂在门把手上,直到自行挣扎脱困。但若不幸咬掉一根电源线,导致正在编撰简历的windows98断电,暴怒的李凌就会将一张报纸卷为棍棒状,将小混蛋按在地板上狂打。
 
2个单身汉对于小猫的思想品德教育无疑是失败的,小混蛋的各种乖张习性都印证了这一点。它开始仇视各种塑料袋和纸张,尤其报纸和卫生纸卷,也从来不肯让人抱。我经常下班回来惊异的发现屋里延绵数十米的手纸报纸碎片,千疮百孔的塑料袋,和一只疯狂且饥饿的小猫。很显然,我们对这个失败的教育案例也是心存愧疚的。李凌大学宿舍的老大从新疆来访那次,极爱猫的他抱着小猫抚摸不止,他说:"猫要有名字的,不然乱叫它就不知道人在叫谁。"我俩连连点头称是,他继续说:"它叫什么名字?"我怯怯的低语,"这个……叫布丁,不过一般……不叫……"李凌则狗腿状陪笑道:"一般……叫它……小混蛋……"
 
快乐的日子一天天过去,小猫变大了,也更淘气了,不会再奶声奶气的叫,也几乎没了恐惧和害怕。然而我的事情似乎不那么顺利,我心情也总是不太好,那时的我,程序内的问题能解决的不多,程序外的问题基本不能解决。我越来越多的忘记给小猫喂食和换灰,它会在我吃泡面的时候跳上茶几舔面碗中的汤,零食薯片和锅巴它也要和我一起分享。我觉得自己想要离开那个环境,却不知道该去哪里,我开始学习java,却是在一种压抑的状态下。
 
布丁已经不需要一步步的爬上床来,晚上我看书的时候,它在对面的床上蹦跳着,抓我床头灯照射下映在墙上的影子,那些放大了的手指和头发,引得它一次次扑下床去。天冷了,没有暖气,需要盖被了,等我熄灯睡觉的时候,它跑来,钻进被窝,再把头伸出来,枕在枕头上,几个月以来都是这样,有时晚上我翻身压到它,它会嘶哑困倦的叫一声,像是抱怨。
 
然而我一天比一天睡得晚,我心里似乎压抑的事情越来越多。终于到了它想睡的时候,我还在看书。它跑过来,想要钻进被子里,烦躁的我头一次动手打了它,出手很重。它看着我叫了几声,就跑到客厅去了。过了一会,我平息下来,下了床,去客厅找它,它团成一团缩在沙发上,看到我出来,就委屈的叫了一声,然后低头,我把它抱回床上,好像就都过去了,对它来说。
 
老妈来我住处看我,和家里冷战了这么久,慢慢也和解了,我不知道该说什么,老妈在洗了我几乎所有的衣物床单,她在屋里来回走着,忙着,小猫就时不时从躲藏的地方冲出来,拍一下她的腿,再躲回去。老妈喜欢这顽劣的小猫,她笑着问我:"我惹到了它了?""我把它带回去吧"我笑笑,还是没说什么。
 
我们清扫了屋子,意外发现沙发底下小猫的宝库,许多瓶盖和花生米聚成了一堆,还有我的一只袜子,李凌要扫走的时候,小猫跑过来,一边叫着,一边使劲闻着地板,于是给它留下了一个瓶盖,其他财产都充公了。我就要离开了,杨砾说理解我,打消了我心理最后那点顾虑,然后下雪了,老弟过来找我玩,我们在楼下打雪仗,我把小猫装在怀里,它惊恐的看着疯狂又开心的人们,最后我把它也扔在雪里,它陷在雪坑里,瞪大了眼,不敢动。
 
我收了一些行李,剩下的东西舅舅帮我收,洗衣机,床,电脑。我翻出姐姐给我买的joycarry,那个红绿相间的背包,布丁被我塞进这个包,它害怕了,都不敢叫。我不确定火车上让不让带猫,去坐汽车,它一直没叫,车开了,我拉开一点拉索,看见它瞪大眼看我,然后极力想出来,我不让,它就委屈的叫了一声,前排的妇女吃惊的转头看我,不过什么也没说。
 
姐来接我,她也极喜欢猫,她说:"让我看看啥样子",我拉开一点拉链,小猫使劲把脑袋探出来,脸颊的毛都紧贴着,脑袋看着尖尖的,它对着姐大声叫,姐高兴的笑着:像个大耗子一样。回了家,我抱着它坐在厨房,妈准备炒菜,跟我说去北京要注意的一些事,我没太听,猫极度惊恐着,它不认识这里,不像小时候那样无知无畏,不管我怎么抚摸都无法让它镇静,青菜下锅时的暴响吓到了它,它从我手里挣扎出去,跳过落地风扇,冲到屋里,跑到床下不肯出来。姐忙着给它准备灰盆和吃的,她看出我担心,说没关系,过几天就好了。
 
过了几天,我拖一个箱子,头一次去北京。李凌正好出差,我们买到一趟车,他带着个笔记本,电池能用1小时,我们玩了会泡泡龙,没电了。相处半年,那次话大概最少,都不知该说什么。
 
三个月后,过年,我再回家,布丁已经习惯家里了,不过也不认识我了。它觉得我陌生,距离总保持2米,它和妈妈最亲,妈妈可以抱它,心情好的时候姐姐可以抱它,最怕老爸,它白天蹲在微波炉上,谁关门它就抓谁,除了老爸。晚上我上网,吃姐给我买的锅巴,猛然低头看到它蹲在我脚下,看着我嚼锅巴的嘴,一声不响。我给它锅巴,它低头吃,和以前一样,我摸了摸它,它还是想不起我,也再不会上床睡觉。
 
其后几年,它都不再亲近我。它已经不会再想起从前,而从前却一直存在在我心里。妈问我它为什么见不得塑料袋和报纸,我仍然不知道该怎么说。姐要生孩子,它被送到姐夫舅舅家,姐舍不得,哭了好几次。后来知道它绝食,跑掉了,姐夫家人一直瞒着,不敢说。妈有次悄悄跟我说起它,说它诡异又聪明,最后叹了口气,说受不了离别,伤了元气,以后再也不养猫。
 
8年了,我确实很有些想那只淘气的布丁,我愿意一辈子留着对这只猫的所有记忆,不管是快乐的,还是悲伤的。
7月16日

干锅毛豆

煮毛豆煮着煮着就忘了,突然闻见糊味,以为笔记本8600m缺陷卡终于成功烧掉,端起来仔细闻了一圈发现不是,赶紧冲到厨房,发现新品干锅毛豆研发成功,而且煤气表报警锁死了……

英雄

昨晚回家走到楼下,过来一位英雄,带着美女,英雄说:“午饭老不够吃,以后中午我加大粪吃一斤半”
敬仰的我魂飞魄散。
7月9日

c++ dlopen

quick note:
 
main:
class A
{
public:
static int CreateInstance(){return 0;};
};
 
plugin.so
 
int create_A_instance()
{
   return A::CreateInstance();
}
 
...
dlopen(plugin.so, RTLD_NOW ...);
problem: error: unreference symbol _ZN1A14CreateInstanceEv
solution: g++ -o main -rdynamic ...
comment: link option -rdynamic exports execuable symbols for .so to search
 
dlsym(handle, "create_A_instance");
problem: returns null
solution1: extern "C" int create_A_instance(){...}...
solution2: dlsym(handle, "_ZN17create_A_instanceEv");
comment: symbols will be mangled by cpp compiler, extern "C" to avoid it or load a mangled name.