Coroutine(2)

公開:2009-12-19 16:55
更新:2020-02-15 04:36
カテゴリ:boost,windows,c++

Coroutineへの探求は少し横道をそれながらもつづいている。
昨日作ったfiberサンプルに少し手を入れてみるとかしながら、Boost.Coroutineのコードをにらめっこしている最中。


// FiberTest.cpp
//
#define  _WIN32_WINNT  0x0600
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <iostream>
#include <exception>
#include <memory>
#include <string>
#include <map>
#include <locale>
#include <fstream>
#include <algorithm>
#include <functional>
#include <boost/shared_ptr.hpp>
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/ptr_container/ptr_container.hpp>
#include "windows.h"
#include "windowsx.h"
#include "tchar.h"
#include "stdio.h"
void main();
void WINAPI FlsCallback(void*lpFlsData);
namespace sf {
struct fiber_resource
{
explicit fiber_resource(int v) : id_(v)
{
std::wcout << boost::wformat(L"リソース%dが作成されました") % v << std::endl;
};
~fiber_resource()
{
std::wcout << boost::wformat(L"リソース%dが削除されました") % id_ << std::endl;
};
private:
int id_;
};
struct fiber : private boost::noncopyable
{
typedef boost::function<void (fiber& self)> fiber_proc_type;
friend void ::main();
fiber(fiber_proc_type& f,int stack_size = 4096)
{
fiber_proc_ = f;
void * param = reinterpret_cast<void *>(this);
context_ = ::CreateFiber(stack_size,fiberProc,param);
id_ = id_count_++;
std::wcout << boost::wformat(L"FiberがID%dで作成されました。") % id() << std::endl;
terminate_ = false;
}
~fiber()
{
if(context_)
{
::DeleteFiber(context_);
}
std::wcout << boost::wformat(L"Fiber:ID%dが削除されました。") % id() << std::endl;
}
// mainのfiberへ制御を戻す。
void yield()
{
::SwitchToFiber(main_fiber_);
}
// まあこれはとりあえず実装
void yield_to(const fiber& f)
{
::SwitchToFiber(f.context());
}
// ファイバーの実行・再開
void run()
{
if(context_){
::SwitchToFiber(context_);
}
}
void * context() const { return context_;}
const int id () const { return id_;}
bool is_terminate() {return terminate_;}
void is_terminate(bool value) {terminate_ = value;}
fiber_proc_type& fiber_proc(){return fiber_proc_;}
void fiber_proc(fiber_proc_type& f) {fiber_proc_ = f;}
private:
bool terminate_;
void* context_;
fiber_proc_type fiber_proc_;
int id_;
static void * main_fiber_;
static int id_count_;
static void WINAPI fiberProc ( void * self)
{
fiber* p = reinterpret_cast<fiber*>(self);
while(1)
{
// 終了していないなら
if(!p->is_terminate())
{
p->fiber_proc()(*p);
p->is_terminate(true);
}
// メインファイバに戻る
p->yield();
}
};
};
void fiber_proc(fiber& self)
{
//このHeapの後始末がどうなるか・・
boost::shared_ptr<sf::fiber_resource> a(new sf::fiber_resource(self.id()));
std::wcout << boost::wformat(L"FiberID%d(1)が実行されました。") % self.id() << std::endl;
self.yield();
std::wcout << boost::wformat(L"FiberID%d(2)が実行されました。") % self.id() << std::endl;
};
}
void * sf::fiber::main_fiber_ = 0;
int sf::fiber::id_count_ = 0;
const int FIBER_MAX = 5;
void main()
{
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF);
DWORD result = FlsAlloc(&FlsCallback);
// mainファイバの保存
// 子fiberから参照されるように。。
sf::fiber::main_fiber_ = ::ConvertThreadToFiber(0);
boost::ptr_vector<sf::fiber> fiber_array;
std::wcout.imbue(std::locale("japanese"));
// fiber配列生成
for(int i = 0; i  < FIBER_MAX;++i)
{
sf::fiber *p = new sf::fiber(sf::fiber::fiber_proc_type(&sf::fiber_proc));
if(p->context())
{
fiber_array.push_back(p);
} else {
delete p;
break;
}
}
// 作成できたfiber数を表示
std::wcout << boost::wformat(L"Fiber は %d 個作れました。") % fiber_array.size() << std::endl;
// 作成したfiberを実行
std::for_each(fiber_array.begin(),fiber_array.end(),boost::bind(&sf::fiber::run,_1));
// 作成したfiberをもう一回実行
std::for_each(fiber_array.begin(),fiber_array.end(),boost::bind(&sf::fiber::run,_1));
fiber_array.clear();
// 終了
fiber_array.clear();
::ConvertFiberToThread();
FlsFree(result);
}
void WINAPI FlsCallback(void*lpFlsData)
{
std::wcout << L"Call Back" << std::endl;
}


ちょっとfiber_procを外出しにして、リソースが開放されるようにしてみたりしているところ。
あとはUMSに備えて、context管理コード部分をポリシーにしてfiberコードを分離できないかな?とか考えたりしている。
まぁ、こんなことしなくてもBoost.Coroutineを使えば簡単に出来るのだが、、性懲りもなく実験してしまっている。
車輪の再発明ってやつだ。
Fiber Local Storageについてはさっぱり使い方が分からない。まあこれの元というかTLSについても知らないのでしょうがないが。
しかし、Fiberの情報って少ないね。。やっぱりobsoleteな香りがプンプンするな。。