C言語でコルーチン(co-routine)(2)

C言語でコルーチンを実装してみる、その2。
前回のコルーチンマクロではローカル変数が使えない、外部から状態を制御できないという問題があった。今回はそれらの問題を解決してみる。まずはサンプルコードを見てほしい。
int hoge(co_routine_t coro){
co_begin(coro);
co_local_valiables{
int a;
int b;
};
co_local.a = 0;
co_local.b = 0;
printf("arg 0x%x\n",(int)co_get_addrword(coro));
while(1){
printf("a=%d b=%d\n",co_local.a++,co_local.b++);
co_yield();
}
co_end;
}

main{
co_routine_t coro;
int stack[32];
coro = co_create(hoge,0xaa,stack,sizeof(stack));
co_call(coro);
co_call(coro);
co_call(coro);
co_call(coro);
}
実行結果は次のとおり。
% a.exe
arg=0xaa
a=0 b=0
a=1 b=1
a=2 b=2
a=3 b=3
前回のコルーチンマクロから変わった所は、コルーチンの関数定義、co_beginマクロに引数を追加、co_local_variables/co_localマクロの追加、co_create/co_call関数の追加ぐらいだろうか。順番に見ていこう。
  • int hoge(co_routine_t coro)
    今回の実装ではコルーチン関数はco_routine_t型の引数とint型の値を返す必要がある。なもんで、呼び出す度に戻り値が変わる関数を作りたい場合には使えない。
  • co_begin,co_end
    毎度おなじみの"おまじない"。
  • co_local_valiables
    ローカル変数の宣言をここで行う。これらの変数はコルーチンが呼び出されている間は消えることはない。ローカル変数へのアクセスはco_localマクロを経由して行う。co_localは単純にスタックのアドレスをローカル変数用の構造体にキャストしているだけ。
  • co_yield()
    このマクロが呼ばれた時点でコルーチンは処理を中断し、関数を抜ける。次に呼ばれた時は、このマクロの次の行から処理が再開される。
  • coro = co_create(hoge,0xaa,stack,sizeof(stack))
    co_create関数を使ってコルーチンを作成する。第1引数にコルーチン関数のポインタ、第2引数にコルーチン関数に渡す引数、第3引数にスタックのアドレス、第4引数にスタックのサイズを渡す。スタックのサイズは最低、sizeof(関数ポインタ)+sizeof(コルーチン関数に渡す引数)+sizeof(int)分必要。作成に成功したらスタックのアドレスがco_routine_t型にキャストされて返される。失敗した場合は0が返る。
  • co_call(coro)
    コルーチン呼び出す。コルーチンでco_yieldマクロが呼ばれたら処理が返ってくる。
ソースコードは長いので続きに載せておくよ。

そうそう、マクロでちまちまやるのは嫌だという人にはPCL(Portable Coroutine Library)というポータブルなコルーチンライブラリがあるよ。PCLはcontext save/restoreでコルーチンを実現しているよ。setjmp、longjmpを駆使するみたい。

Portable Coroutine Library
http://www.xmailserver.org/libpcl.html
coroutine.h
#ifndef  __COROUTINE_H__
#define __COROUTINE_H__

typedef struct coroutine* coroutine_t;
typedef int(*co_entry_t)(coroutine_t);
typedef void* co_addrword_t;
typedef void* co_stack_t;
typedef int co_size_t;
typedef int co_resume_t;

struct coroutine {
co_entry_t entry;
co_addrword_t addrword;
co_resume_t resume;
};

#define CO_INITIALALIZE (0)
#define CO_INVALID (-1)

#define co_begin(coro) \
void *__stack_addr = (void*)((coro)+1); \
switch((coro)->resume){ \
case CO_INITIALALIZE:;

#define co_yield() \
do{ \
return(__LINE__); \
case __LINE__:; \
}while(0)

#define co_exit() \
return(CO_INVALID)

#define co_end \
} \
co_exit()

#define co_local_valiables \
typedef struct co_local_valiables* co_local_valiables_t; \
struct co_local_valiables

#define co_local \
(*((co_local_valiables_t)__stack_addr))

#define co_get_addrword(coro) \
((coro)->addrword)

coroutine_t co_create(co_entry_t entry,co_addrword_t addrword,co_stack_t stack,co_size_t size);
co_resume_t co_call(coroutine_t coro);
int co_is_valid(coroutine_t coro);

#endif

coroutine.c
#include "coroutine.h"

coroutine_t co_create(co_entry_t entry,co_addrword_t addrword,co_stack_t stack,co_size_t size)
{
struct coroutine *coro = (struct coroutine*)stack;
if(coro && (size>=sizeof(*coro))){
coro->entry = entry;
coro->addrword = addrword;
coro->resume = CO_INITIALALIZE;
return (coroutine_t)coro;
}
return 0;
}

co_resume_t co_call(coroutine_t coro)
{
co_resume_t ret = CO_INVALID;
if(co_is_valid(coro)){
ret = coro->entry(coro);
coro->resume = ret;
}
return ret;
}

int co_is_valid(coroutine_t coro)
{
return (coro && coro->entry && (coro->resume!=CO_INVALID));
}

コメント

Secret

Ads by Google
最近の記事
カテゴリ
スタバカップアンプ (6)
電光掲示板 (2)
Arduino (19)
Make: (9)
太陽電池 (12)
ニキシー管 (17)
PICライタ (15)
自作USBデバイス (6)
電波時計 (3)
Ogg Vorbis Player (12)
電子工作 (67)
Xfind (6)
Cocoa (18)
Bluetooth (3)
twitter (4)
Coroutine (4)
本 (3)
未分類 (21)
テルミン (3)
最近のコメント
arms22:Arduinoで遊ぼう - Arduinoで作るシンセサイザー (06/27)
yamadanohito:Arduinoで遊ぼう - Arduinoで作るシンセサイザー (06/27)
arms22:twifp 0.1 リリースしました。 (06/21)
arms22:Arduinoで遊ぼう - 自作プロトシールド (06/08)
きぃたん:Arduinoで遊ぼう - 自作プロトシールド (06/08)
arms22:Arduinoで遊ぼう - Arduinoで作るシンセサイザー (06/01)
saku:Arduinoで遊ぼう - Arduinoで作るシンセサイザー (06/01)
arms22:Make: Tokyo Meeting 03 Day 2 (05/28)
最近のトラックバック
以前の記事
リンク
タグ
プライバシーポリシー
当サイトでは、第三者配信による広告サービスを利用しています。このような広告配信事業者は、ユーザーの興味に応じた商品やサービスの広告を表示するため、当サイトや他サイトへのアクセスに関する情報 (氏名、住所、メール アドレス、電話番号は含まれません) を使用することがあります。このプロセスの詳細やこのような情報が広告配信事業者に使用されないようにする方法については、ここをクリックしてください。