Fibers

公開:2009-12-18 14:56
更新:2020-02-15 04:36
カテゴリ:boost,windows,c++

ちょっとFiberについてテストをしてみた。
fiberルーチン上でHeapを確保したらどうなるか?


// FiberTest.cpp
//
#define  _WIN32_WINNT  0x0600
#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/ptr_container/ptr_container.hpp>
#include "windows.h"
#include "windowsx.h"
#include "tchar.h"
#include "stdio.h"
void main();
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
{
friend void ::main();
fiber(LPFIBER_START_ROUTINE f = fiberProc,int stack_size = 4096)
{
void * param = reinterpret_cast<void *>(this);
context_ = ::CreateFiber(stack_size,f,param);
id_ = id_count_++;
std::wcout << boost::wformat(L"FiberがID%dで作成されました。") % id() << std::endl;
}
~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_;}
void fiber_proc()
{
//このHeapの後始末がどうなるか・・
boost::shared_ptr<sf::fiber_resource> a(new sf::fiber_resource(id()));
std::wcout << boost::wformat(L"FiberID%d(1)が実行されました。") % id() << std::endl;
yield();
std::wcout << boost::wformat(L"FiberID%d(2)が実行されました。") % id() << std::endl;
yield();
};
private:
void* context_;
int id_;
static void * main_fiber_;
static int id_count_;
static void WINAPI fiberProc ( void * self)
{
fiber* p = reinterpret_cast<fiber*>(self);
p->fiber_proc();
};
};
}
void * sf::fiber::main_fiber_ = 0;
int sf::fiber::id_count_ = 0;
const int FIBER_MAX = 5;
void main()
{
// 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();
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));
// 終了
::ConvertFiberToThread();
}


実行結果。

FiberがID0で作成されました。
FiberがID1で作成されました。
FiberがID2で作成されました。
FiberがID3で作成されました。
FiberがID4で作成されました。
Fiber は 5 個作れました。
リソース0が作成されました
FiberID0(1)が実行されました。
リソース1が作成されました
FiberID1(1)が実行されました。
リソース2が作成されました
FiberID2(1)が実行されました。
リソース3が作成されました
FiberID3(1)が実行されました。
リソース4が作成されました
FiberID4(1)が実行されました。
FiberID0(2)が実行されました。
FiberID1(2)が実行されました。
FiberID2(2)が実行されました。
FiberID3(2)が実行されました。
FiberID4(2)が実行されました。
Fiber:ID0が削除されました。
Fiber:ID1が削除されました。
Fiber:ID2が削除されました。
Fiber:ID3が削除されました。
Fiber:ID4が削除されました。

予想どおり、fiberルーチンはリターンされないので、fiber_proc内でヒープに確保したリソースは開放されないことがわかった。
さて、どうすればいいか・・。