Continuation with Boost.Context

Akira Takahashi
Akira TakahashiProgrammer en Preferred Networks, Inc.
Boost.Contextで継続


                 高橋 晶(Akira Takahashi)
         id:faith_and_brave/@cpp_akira

     2012/04/08(土) Boost.Contextオンリーイベント
Boost.Context
• Oliver Kowalkeが作ったBoost.Fiberというライブラリの、コンテ
  キストスイッチ部分を切り出したライブラリ
• Boost 1.49.0時点でtrunkに入ってる
• 継続(continuation)、ファイバー(fiber)、コルーチン(coroutine)、
  マイクロスレッド(microthread)などなど、いろんな呼び名のあ
  る技術を実現することができる。
資料などのあるところ

• Boost.Context について調べた – melpon日記
  http://d.hatena.ne.jp/melpon/20111213/1323704464
• ドキュメント
  http://ok73.ok.funpic.de/boost/libs/context/doc/html/
• ソースコード
  https://github.com/ryppl/boost-svn
  boost/context以下、およびlibs/context以下にソースコードお
  よびサンプル、テストがある。
継続は何をするのか
• スレッドがやってることを自前でやる

• ユニプロセッサを考える。
  スレッドは一つのプロセッサで複数の処理を同時に実行する
  ために、
  「少し実行してコンテキスト内のスタックを一旦保存して別な
  コンテキストに切り替えて…」
  ということをおこなっている。

• 継続と呼ばれる概念では、このコンテキストスイッチを自前で
  行うことにより、いくつかのケースのプログラムを自然な流れ
  で書くことができる。
基本的な使い方
• boost::contexts::contextというクラスのコンストラクタで以下を
  設定する:
  –   resume()時に呼ばれる関数
  –   スタックサイズ
  –   最後にデストラクタを呼ぶかどうか
  –   呼び元に必ず戻るかどうか
• context::suspend()メンバ関数
  – コンテキストを中断
• context::resume()メンバ関数
  – コンテキストを再開
• context::start()メンバ関数
  – コンテキストを開始(2回目以降はresume()を呼ぶ)
継続クラス(contextのラッパー)
#include <boost/context/all.hpp>
#include <boost/function.hpp>
#include <boost/utility/value_init.hpp>

class continuation {
  boost::contexts::context ctx_;
  boost::function<void(continuation&)> fn_;
  boost::initialized<bool> started_;
  boost::initialized<bool> is_break_;

 void trampoline_() { fn_(*this); }

public:
  continuation() {}
継続クラス(contextのラッパー)
continuation(boost::function<void(continuation&)> const& fn)
{
  ctx_ = boost::contexts::context(
              &continuation::trampoline_,
              this,
              boost::contexts::default_stacksize(),
              boost::contexts::stack_unwind,
              boost::contexts::return_to_caller);
   fn_ = fn;
}
継続クラス(contextのラッパー)
continuation& operator=(continuation&& other)
{
  // thisに依存してるとmoveできないので作り直し
  ctx_ = boost::contexts::context(
              &continuation::trampoline_,
              this,
              boost::contexts::default_stacksize(),
              boost::contexts::stack_unwind,
              boost::contexts::return_to_caller);

    fn_ = boost::move(other.fn_);
    started_ = boost::move(other.started_);
    is_break_ = boost::move(other.is_break_);
    return *this;
}
継続クラス(contextのラッパー)
int resume()
{
  if (!started_.data()) {
    started_.data() = true;
    is_break_.data() = false;
    return ctx_.start();
  }
  else { return ctx_.resume(); }
}

int restart()
{
  started_.data() = false;
  is_break_.data() = false;
  ctx_ = boost::contexts::context(
              &continuation::trampoline_,
              this,
              boost::contexts::default_stacksize(),
              boost::contexts::stack_unwind,
              boost::contexts::return_to_caller);
  return resume();
}
継続クラス(contextのラッパー)
     int suspend(int vp = 0)
     { return ctx_.suspend(vp); }

     int suspend_break(int vp = 0)
     {
       is_break_.data() = true;
       return ctx_.suspend(vp);
     }

     bool is_complete() const
     { return is_break_.data() || ctx_.is_complete(); }

     void unwind_stack() { ctx_.unwind_stack(); }
};
考えられる基本的なユースケース

• 非同期処理の逐次化
 – コールバック地獄の解消
• (ゲームループなど)継続的に実行する処理の逐次化
 – ファイルの読み込み、シューティングゲームの弾道など
• リスト処理の遅延評価のようなこと
 – C#のyield return/IEnumerableみたいなこと
 – イテレータ/ジェネレータ
非同期処理の逐次化

通常の非同期処理                継続バージョン
void foo()              async_xxx(…, complete);
{                       suspend();
  async_xxx(…, bar);
}                       async_xxx(…, complete);
                        suspend();
void bar()
{                       …
  async_xxx(…, hoge);
}                       void complete()
                        {
void hoge();              resume();
                        }
コールバック関数をあちこちに書く
                        非同期処理を実行
                        → 中断
                        → コールバック関数内で再開を指示
非同期処理の逐次化
class Client {
  io_service& io_service_;
  socket socket_;

 continuation cont_;
 error_code ec_;

public:
  Client(io_service& io_service)
    : io_service_(io_service), socket_(io_service)
  {
    cont_ = continuation(bind(&Client::do_, this));
  }

 void start()
 {
   ec_ = error_code();
   cont_.resume();
 }
 …
非同期処理の逐次化
private:
  void do_()
  {
    while (!ec_) {
      const string req = "ping¥n";
      async_write(socket_, buffer(req), bind(…, completion_handler));
      cont_.suspend();

         if (ec_) break;

         streambuf rep;
         async_read_until(socket_, rep, '¥n', bind(…, completion_handler));
         cont_.suspend();

         if (ec_) break;

         cout << buffer_cast<const char*>(rep.data());
     }

     cout << ec_.message() << endl;
 }
非同期処理の逐次化
     void completion_handler(const error_code& ec)
     {
       ec_ = ec;
       cont_.resume();
     }
};


asio::io_service io_service;
Client client(io_service);

client.start();

io_service.run();




             http://d.hatena.ne.jp/faith_and_brave/20120329/1333008572
(ゲームループなど)継続的に実行する処理の逐次化

通常の定期実行処理                   継続バージョン
// 定期的に呼ばれる関数               // 定期的に呼ばれる関数
void updatePosition()       void updatePosition()
{                           {
  switch (count_) {           x = 0; y = 0; suspend();
    case 0: x = 0; y = 0;     x = 1; y = 0; suspend();
    case 1: x = 1; y = 0;     x = 1; y = 1; suspend();
    case 2: x = 1; y = 1;     x = 0; y = 1; suspend();
    case 3: x = 0; y = 1;     x = 0; y = 0; suspend();
    case 4: x = 0; y = 0;   }
  }
  count_++;
}


どこまで実行したかを自分で覚              次回呼ばれたときに次の行を実行
えておき、次回呼ばれたときに              (自然な流れ)
状態に合わせた地点から実行
(ゲームループなど)継続的に実行する処理の逐次化
class Game {
  vector<string> data;
  continuation cont;
public:
  Game()
    : cont(bind(&Game::load_file, this)) {}

 void update()
 {
   if (!cont.is_complete()) {
       cont.resume();
       cout << data.back() << endl;
   }
 }

 void draw() {}
(ゲームループなど)継続的に実行する処理の逐次化
private:
   void load_file()
   {
     ifstream file("a.txt");
     string line;
     while (std::getline(file, line)) {
       data.push_back(line);
       if (file.peek() != EOF) {
         cont.suspend();
       }
     }
   }
};
(ゲームループなど)継続的に実行する処理の逐次化
const milliseconds timer_duration(static_cast<int>(1.0 / 60.0 * 1000));

void on_timer(Game& game, steady_timer& timer)
{
  game.update();
  game.draw();

    timer.expires_from_now(timer_duration);
    timer.async_wait(
      bind(&on_timer, ref(game), ref(timer)));
}

Game game;
io_service io_service;
steady_timer timer(io_service);

timer.expires_from_now(timer_duration);
timer.async_wait(
         bind(&on_timer, ref(game), ref(timer)));

io_service.run();
         http://d.hatena.ne.jp/faith_and_brave/20120312/1331537869
リスト処理の遅延評価
// nから始まる無限リスト
void enum_from(continuation& cont, int n)
{
  for (int i = n;; ++i) {
    cont.suspend(i);
  }
}

int main()
{
  continuation cont(bind(enum_from, _1, 3));

    // 3から始まる無限リストから先頭10個を取り出す
    for (int i = 0; i < 10; ++i) {
      int n = cont.resume();
      cout << n << endl;
    }
}

          これを抽象化したBoost.Enumeratorというのが開発されている
          https://github.com/jamboree/boost.enumerator
中断可能なアルゴリズム

• 中断可能なアルゴリズムを後付で作れないかと試した。
  Boost.Graphでの最短経路の計算を外部から少しずつ実行す
  る。
• Boost.GraphにはEvent Visitorというのがあり、最短経路計算
  において「点が交差したときに指定された関数を呼ぶ」という
  ようなことができる。
  ここに継続を置いてみた。
class path_history_visitor {
  continuation& cont;
public:
  typedef boost::on_discover_vertex event_filter;

     path_history_visitor(continuation& cont) : cont(cont) {}

     template<typename Vertex, typename Graph>
     void operator()(Vertex v, const Graph&)
     {
       std::cout << Names[v] << ' ';
       cont.suspend();
     }
};
int main()
{
  const Graph g = make_graph();
  const Vertex from = S;

    continuation cont = [&](continuation& cont) {
      path_history_visitor vis(cont);

      boost::dijkstra_shortest_paths(g, from,
             boost::visitor(boost::make_dijkstra_visitor(vis)));
    };

    while (!cont.is_complete()) {
      cont.resume();
    }
}



    ポイントはラムダ式とEvent Visitor。
    Event Visitorは、アルゴリズムの中断ポイントを定義するのに使用できる。
    ラムダは、既存のプログラムをBoost.Contextで使用できるコンテキストに変換するのに使
    用できる。
その他
• ambを実装した
  https://github.com/faithandbrave/Keizoku/tree/master/amb

• Boost.CoroutineがBoost.Contextで書き直されるらしい
  「Boost.Coroutineを用いてステートマシンを解決する – C++Now! 2012」
  https://sites.google.com/site/boostjp/cppnow/2012#coroutine

• Scala設計者のMartin Odersky先生が「Observerパターンやめて継続使お
  うぜ」という論文を書いてる
  「Deprecating the Observer Pattern」
  http://lampwww.epfl.ch/~imaier/pub/DeprecatingObserversTR2010.pdf
1 de 24

Recomendados

Boost.Coroutine por
Boost.CoroutineBoost.Coroutine
Boost.Coroutinemelpon
23.8K vistas44 diapositivas
20分くらいでわかった気分になれるC++20コルーチン por
20分くらいでわかった気分になれるC++20コルーチン20分くらいでわかった気分になれるC++20コルーチン
20分くらいでわかった気分になれるC++20コルーチンyohhoy
12.9K vistas31 diapositivas
コルーチンの使い方 por
コルーチンの使い方コルーチンの使い方
コルーチンの使い方Naohiro Yoshikawa
14.7K vistas52 diapositivas
C++でHello worldを書いてみた por
C++でHello worldを書いてみたC++でHello worldを書いてみた
C++でHello worldを書いてみたfirewood
6.8K vistas22 diapositivas
Siv3Dで楽しむゲームとメディアアート開発 por
Siv3Dで楽しむゲームとメディアアート開発Siv3Dで楽しむゲームとメディアアート開発
Siv3Dで楽しむゲームとメディアアート開発Ryo Suzuki
7.8K vistas71 diapositivas
C++14 Overview por
C++14 OverviewC++14 Overview
C++14 OverviewAkira Takahashi
11.8K vistas33 diapositivas

Más contenido relacionado

La actualidad más candente

コルーチンを使おう por
コルーチンを使おうコルーチンを使おう
コルーチンを使おうamusementcreators
8.8K vistas17 diapositivas
Synthesijer and Synthesijer.Scala in HLS-friends 201512 por
Synthesijer and Synthesijer.Scala in HLS-friends 201512Synthesijer and Synthesijer.Scala in HLS-friends 201512
Synthesijer and Synthesijer.Scala in HLS-friends 201512Takefumi MIYOSHI
1.3K vistas58 diapositivas
新しい並列for構文のご提案 por
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案yohhoy
29.6K vistas71 diapositivas
C++ ポインタ ブートキャンプ por
C++ ポインタ ブートキャンプC++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプKohsuke Yuasa
11K vistas145 diapositivas
Effective modern-c++#9 por
Effective modern-c++#9Effective modern-c++#9
Effective modern-c++#9Tatsuki SHIMIZU
858 vistas36 diapositivas
競技プログラミングのためのC++入門 por
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門natrium11321
32.9K vistas63 diapositivas

La actualidad más candente(20)

Synthesijer and Synthesijer.Scala in HLS-friends 201512 por Takefumi MIYOSHI
Synthesijer and Synthesijer.Scala in HLS-friends 201512Synthesijer and Synthesijer.Scala in HLS-friends 201512
Synthesijer and Synthesijer.Scala in HLS-friends 201512
Takefumi MIYOSHI1.3K vistas
新しい並列for構文のご提案 por yohhoy
新しい並列for構文のご提案新しい並列for構文のご提案
新しい並列for構文のご提案
yohhoy29.6K vistas
C++ ポインタ ブートキャンプ por Kohsuke Yuasa
C++ ポインタ ブートキャンプC++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプ
Kohsuke Yuasa11K vistas
競技プログラミングのためのC++入門 por natrium11321
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
natrium11321 32.9K vistas
競技プログラミングにおけるコードの書き方とその利便性 por Hibiki Yamashiro
競技プログラミングにおけるコードの書き方とその利便性競技プログラミングにおけるコードの書き方とその利便性
競技プログラミングにおけるコードの書き方とその利便性
Hibiki Yamashiro18.2K vistas
Visual C++で使えるC++11 por nekko1119
Visual C++で使えるC++11Visual C++で使えるC++11
Visual C++で使えるC++11
nekko111936.8K vistas
Effective Modern C++ 読書会 Item 35 por Keisuke Fukuda
Effective Modern C++ 読書会 Item 35Effective Modern C++ 読書会 Item 35
Effective Modern C++ 読書会 Item 35
Keisuke Fukuda11K vistas
Cloud TPU Driver API ソースコード解析 por Mr. Vengineer
Cloud TPU Driver API ソースコード解析Cloud TPU Driver API ソースコード解析
Cloud TPU Driver API ソースコード解析
Mr. Vengineer532 vistas
Javaとかc#とか最近の言語使いのためのc++入門 por potimarimo
Javaとかc#とか最近の言語使いのためのc++入門Javaとかc#とか最近の言語使いのためのc++入門
Javaとかc#とか最近の言語使いのためのc++入門
potimarimo7.4K vistas
BoostAsioで可読性を求めるのは間違っているだろうか por Yuki Miyatake
BoostAsioで可読性を求めるのは間違っているだろうかBoostAsioで可読性を求めるのは間違っているだろうか
BoostAsioで可読性を求めるのは間違っているだろうか
Yuki Miyatake14.3K vistas
組み込みでこそC++を使う10の理由 por kikairoya
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
kikairoya27K vistas
Map por kikairoya
MapMap
Map
kikairoya10.2K vistas
クロージャデザインパターン por Moriharu Ohzu
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターン
Moriharu Ohzu19.6K vistas
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜 por 勝成 鈴江
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
勝成 鈴江5.2K vistas

Destacado

C++0xの概要(デブサミ2010) por
C++0xの概要(デブサミ2010)C++0xの概要(デブサミ2010)
C++0xの概要(デブサミ2010)Akira Takahashi
1.3K vistas26 diapositivas
Bjarne dont speaking por
Bjarne dont speakingBjarne dont speaking
Bjarne dont speakingAkira Takahashi
1.8K vistas31 diapositivas
C++ Now 2012 report por
C++ Now 2012 reportC++ Now 2012 report
C++ Now 2012 reportAkira Takahashi
1.1K vistas29 diapositivas
C++0x Variadic Type List por
C++0x Variadic Type ListC++0x Variadic Type List
C++0x Variadic Type ListAkira Takahashi
2.7K vistas20 diapositivas
俺的英語・中国語の勉強法を晒してみる 2011 10-12 por
俺的英語・中国語の勉強法を晒してみる 2011 10-12俺的英語・中国語の勉強法を晒してみる 2011 10-12
俺的英語・中国語の勉強法を晒してみる 2011 10-12俊仁 小林
1.8K vistas12 diapositivas
Boost Tour 1.50.0 All por
Boost Tour 1.50.0 AllBoost Tour 1.50.0 All
Boost Tour 1.50.0 AllAkira Takahashi
8.3K vistas110 diapositivas

Destacado(6)

C++0xの概要(デブサミ2010) por Akira Takahashi
C++0xの概要(デブサミ2010)C++0xの概要(デブサミ2010)
C++0xの概要(デブサミ2010)
Akira Takahashi1.3K vistas
俺的英語・中国語の勉強法を晒してみる 2011 10-12 por 俊仁 小林
俺的英語・中国語の勉強法を晒してみる 2011 10-12俺的英語・中国語の勉強法を晒してみる 2011 10-12
俺的英語・中国語の勉強法を晒してみる 2011 10-12
俊仁 小林1.8K vistas

Similar a Continuation with Boost.Context

Pfi Seminar 2010 1 7 por
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Preferred Networks
3.4K vistas60 diapositivas
Boost9 session por
Boost9 sessionBoost9 session
Boost9 sessionfreedom404
1.8K vistas36 diapositivas
x86とコンテキストスイッチ por
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチMasami Ichikawa
8.6K vistas76 diapositivas
Scalaの限定継続の応用と基本 por
Scalaの限定継続の応用と基本Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本Kota Mizushima
1.6K vistas96 diapositivas
Scalaの限定継続の応用と基本(改訂版) por
Scalaの限定継続の応用と基本(改訂版)Scalaの限定継続の応用と基本(改訂版)
Scalaの限定継続の応用と基本(改訂版)Kota Mizushima
885 vistas96 diapositivas
for JSDeferred Code Reading por
for JSDeferred Code Readingfor JSDeferred Code Reading
for JSDeferred Code ReadingKenichirou Oyama
642 vistas18 diapositivas

Similar a Continuation with Boost.Context(20)

Boost9 session por freedom404
Boost9 sessionBoost9 session
Boost9 session
freedom4041.8K vistas
x86とコンテキストスイッチ por Masami Ichikawa
x86とコンテキストスイッチx86とコンテキストスイッチ
x86とコンテキストスイッチ
Masami Ichikawa8.6K vistas
Scalaの限定継続の応用と基本 por Kota Mizushima
Scalaの限定継続の応用と基本Scalaの限定継続の応用と基本
Scalaの限定継続の応用と基本
Kota Mizushima1.6K vistas
Scalaの限定継続の応用と基本(改訂版) por Kota Mizushima
Scalaの限定継続の応用と基本(改訂版)Scalaの限定継続の応用と基本(改訂版)
Scalaの限定継続の応用と基本(改訂版)
Kota Mizushima885 vistas
SystemC Tutorial por kocha2012
SystemC TutorialSystemC Tutorial
SystemC Tutorial
kocha20129.1K vistas
Flow.js por uupaa
Flow.jsFlow.js
Flow.js
uupaa7.8K vistas
ラボユース最終成果報告会(Web公開版) por Shinichi Awamoto
ラボユース最終成果報告会(Web公開版)ラボユース最終成果報告会(Web公開版)
ラボユース最終成果報告会(Web公開版)
Shinichi Awamoto5.1K vistas
Dive into RTS - another side por Kiwamu Okabe
Dive into RTS - another sideDive into RTS - another side
Dive into RTS - another side
Kiwamu Okabe1.4K vistas
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する por Yoshifumi Kawai
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Yoshifumi Kawai74.7K vistas
ぱっと見でわかるC++11 por えぴ 福田
ぱっと見でわかるC++11ぱっと見でわかるC++11
ぱっと見でわかるC++11
えぴ 福田1.6K vistas
PostgreSQL - C言語によるユーザ定義関数の作り方 por Satoshi Nagayasu
PostgreSQL - C言語によるユーザ定義関数の作り方PostgreSQL - C言語によるユーザ定義関数の作り方
PostgreSQL - C言語によるユーザ定義関数の作り方
Satoshi Nagayasu11.5K vistas
Node.js × 音声認識 - 東京Node学園 2012 LT枠 6番目 por hecomi
Node.js × 音声認識 - 東京Node学園 2012 LT枠 6番目Node.js × 音声認識 - 東京Node学園 2012 LT枠 6番目
Node.js × 音声認識 - 東京Node学園 2012 LT枠 6番目
hecomi 10.3K vistas
Linuxのプロセススケジューラ(Reading the Linux process scheduler) por Hiraku Toyooka
Linuxのプロセススケジューラ(Reading the Linux process scheduler)Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Hiraku Toyooka11.9K vistas
Android Lecture #03 @PRO&BSC Inc. por Yuki Higuchi
Android Lecture #03 @PRO&BSC Inc.Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.
Yuki Higuchi664 vistas

Más de Akira Takahashi

Cpp20 overview language features por
Cpp20 overview language featuresCpp20 overview language features
Cpp20 overview language featuresAkira Takahashi
890 vistas28 diapositivas
Cppmix 02 por
Cppmix 02Cppmix 02
Cppmix 02Akira Takahashi
675 vistas7 diapositivas
Cppmix 01 por
Cppmix 01Cppmix 01
Cppmix 01Akira Takahashi
1K vistas11 diapositivas
Modern C++ Learning por
Modern C++ LearningModern C++ Learning
Modern C++ LearningAkira Takahashi
18.5K vistas12 diapositivas
cpprefjp documentation por
cpprefjp documentationcpprefjp documentation
cpprefjp documentationAkira Takahashi
1.3K vistas23 diapositivas
C++1z draft por
C++1z draftC++1z draft
C++1z draftAkira Takahashi
6.6K vistas67 diapositivas

Más de Akira Takahashi(20)

Cpp20 overview language features por Akira Takahashi
Cpp20 overview language featuresCpp20 overview language features
Cpp20 overview language features
Akira Takahashi890 vistas
C++14 solve explicit_default_constructor por Akira Takahashi
C++14 solve explicit_default_constructorC++14 solve explicit_default_constructor
C++14 solve explicit_default_constructor
Akira Takahashi7.3K vistas

Continuation with Boost.Context

  • 1. Boost.Contextで継続 高橋 晶(Akira Takahashi) id:faith_and_brave/@cpp_akira 2012/04/08(土) Boost.Contextオンリーイベント
  • 2. Boost.Context • Oliver Kowalkeが作ったBoost.Fiberというライブラリの、コンテ キストスイッチ部分を切り出したライブラリ • Boost 1.49.0時点でtrunkに入ってる • 継続(continuation)、ファイバー(fiber)、コルーチン(coroutine)、 マイクロスレッド(microthread)などなど、いろんな呼び名のあ る技術を実現することができる。
  • 3. 資料などのあるところ • Boost.Context について調べた – melpon日記 http://d.hatena.ne.jp/melpon/20111213/1323704464 • ドキュメント http://ok73.ok.funpic.de/boost/libs/context/doc/html/ • ソースコード https://github.com/ryppl/boost-svn boost/context以下、およびlibs/context以下にソースコードお よびサンプル、テストがある。
  • 4. 継続は何をするのか • スレッドがやってることを自前でやる • ユニプロセッサを考える。 スレッドは一つのプロセッサで複数の処理を同時に実行する ために、 「少し実行してコンテキスト内のスタックを一旦保存して別な コンテキストに切り替えて…」 ということをおこなっている。 • 継続と呼ばれる概念では、このコンテキストスイッチを自前で 行うことにより、いくつかのケースのプログラムを自然な流れ で書くことができる。
  • 5. 基本的な使い方 • boost::contexts::contextというクラスのコンストラクタで以下を 設定する: – resume()時に呼ばれる関数 – スタックサイズ – 最後にデストラクタを呼ぶかどうか – 呼び元に必ず戻るかどうか • context::suspend()メンバ関数 – コンテキストを中断 • context::resume()メンバ関数 – コンテキストを再開 • context::start()メンバ関数 – コンテキストを開始(2回目以降はresume()を呼ぶ)
  • 6. 継続クラス(contextのラッパー) #include <boost/context/all.hpp> #include <boost/function.hpp> #include <boost/utility/value_init.hpp> class continuation { boost::contexts::context ctx_; boost::function<void(continuation&)> fn_; boost::initialized<bool> started_; boost::initialized<bool> is_break_; void trampoline_() { fn_(*this); } public: continuation() {}
  • 7. 継続クラス(contextのラッパー) continuation(boost::function<void(continuation&)> const& fn) { ctx_ = boost::contexts::context( &continuation::trampoline_, this, boost::contexts::default_stacksize(), boost::contexts::stack_unwind, boost::contexts::return_to_caller); fn_ = fn; }
  • 8. 継続クラス(contextのラッパー) continuation& operator=(continuation&& other) { // thisに依存してるとmoveできないので作り直し ctx_ = boost::contexts::context( &continuation::trampoline_, this, boost::contexts::default_stacksize(), boost::contexts::stack_unwind, boost::contexts::return_to_caller); fn_ = boost::move(other.fn_); started_ = boost::move(other.started_); is_break_ = boost::move(other.is_break_); return *this; }
  • 9. 継続クラス(contextのラッパー) int resume() { if (!started_.data()) { started_.data() = true; is_break_.data() = false; return ctx_.start(); } else { return ctx_.resume(); } } int restart() { started_.data() = false; is_break_.data() = false; ctx_ = boost::contexts::context( &continuation::trampoline_, this, boost::contexts::default_stacksize(), boost::contexts::stack_unwind, boost::contexts::return_to_caller); return resume(); }
  • 10. 継続クラス(contextのラッパー) int suspend(int vp = 0) { return ctx_.suspend(vp); } int suspend_break(int vp = 0) { is_break_.data() = true; return ctx_.suspend(vp); } bool is_complete() const { return is_break_.data() || ctx_.is_complete(); } void unwind_stack() { ctx_.unwind_stack(); } };
  • 11. 考えられる基本的なユースケース • 非同期処理の逐次化 – コールバック地獄の解消 • (ゲームループなど)継続的に実行する処理の逐次化 – ファイルの読み込み、シューティングゲームの弾道など • リスト処理の遅延評価のようなこと – C#のyield return/IEnumerableみたいなこと – イテレータ/ジェネレータ
  • 12. 非同期処理の逐次化 通常の非同期処理 継続バージョン void foo() async_xxx(…, complete); { suspend(); async_xxx(…, bar); } async_xxx(…, complete); suspend(); void bar() { … async_xxx(…, hoge); } void complete() { void hoge(); resume(); } コールバック関数をあちこちに書く 非同期処理を実行 → 中断 → コールバック関数内で再開を指示
  • 13. 非同期処理の逐次化 class Client { io_service& io_service_; socket socket_; continuation cont_; error_code ec_; public: Client(io_service& io_service) : io_service_(io_service), socket_(io_service) { cont_ = continuation(bind(&Client::do_, this)); } void start() { ec_ = error_code(); cont_.resume(); } …
  • 14. 非同期処理の逐次化 private: void do_() { while (!ec_) { const string req = "ping¥n"; async_write(socket_, buffer(req), bind(…, completion_handler)); cont_.suspend(); if (ec_) break; streambuf rep; async_read_until(socket_, rep, '¥n', bind(…, completion_handler)); cont_.suspend(); if (ec_) break; cout << buffer_cast<const char*>(rep.data()); } cout << ec_.message() << endl; }
  • 15. 非同期処理の逐次化 void completion_handler(const error_code& ec) { ec_ = ec; cont_.resume(); } }; asio::io_service io_service; Client client(io_service); client.start(); io_service.run(); http://d.hatena.ne.jp/faith_and_brave/20120329/1333008572
  • 16. (ゲームループなど)継続的に実行する処理の逐次化 通常の定期実行処理 継続バージョン // 定期的に呼ばれる関数 // 定期的に呼ばれる関数 void updatePosition() void updatePosition() { { switch (count_) { x = 0; y = 0; suspend(); case 0: x = 0; y = 0; x = 1; y = 0; suspend(); case 1: x = 1; y = 0; x = 1; y = 1; suspend(); case 2: x = 1; y = 1; x = 0; y = 1; suspend(); case 3: x = 0; y = 1; x = 0; y = 0; suspend(); case 4: x = 0; y = 0; } } count_++; } どこまで実行したかを自分で覚 次回呼ばれたときに次の行を実行 えておき、次回呼ばれたときに (自然な流れ) 状態に合わせた地点から実行
  • 17. (ゲームループなど)継続的に実行する処理の逐次化 class Game { vector<string> data; continuation cont; public: Game() : cont(bind(&Game::load_file, this)) {} void update() { if (!cont.is_complete()) { cont.resume(); cout << data.back() << endl; } } void draw() {}
  • 18. (ゲームループなど)継続的に実行する処理の逐次化 private: void load_file() { ifstream file("a.txt"); string line; while (std::getline(file, line)) { data.push_back(line); if (file.peek() != EOF) { cont.suspend(); } } } };
  • 19. (ゲームループなど)継続的に実行する処理の逐次化 const milliseconds timer_duration(static_cast<int>(1.0 / 60.0 * 1000)); void on_timer(Game& game, steady_timer& timer) { game.update(); game.draw(); timer.expires_from_now(timer_duration); timer.async_wait( bind(&on_timer, ref(game), ref(timer))); } Game game; io_service io_service; steady_timer timer(io_service); timer.expires_from_now(timer_duration); timer.async_wait( bind(&on_timer, ref(game), ref(timer))); io_service.run(); http://d.hatena.ne.jp/faith_and_brave/20120312/1331537869
  • 20. リスト処理の遅延評価 // nから始まる無限リスト void enum_from(continuation& cont, int n) { for (int i = n;; ++i) { cont.suspend(i); } } int main() { continuation cont(bind(enum_from, _1, 3)); // 3から始まる無限リストから先頭10個を取り出す for (int i = 0; i < 10; ++i) { int n = cont.resume(); cout << n << endl; } } これを抽象化したBoost.Enumeratorというのが開発されている https://github.com/jamboree/boost.enumerator
  • 21. 中断可能なアルゴリズム • 中断可能なアルゴリズムを後付で作れないかと試した。 Boost.Graphでの最短経路の計算を外部から少しずつ実行す る。 • Boost.GraphにはEvent Visitorというのがあり、最短経路計算 において「点が交差したときに指定された関数を呼ぶ」という ようなことができる。 ここに継続を置いてみた。
  • 22. class path_history_visitor { continuation& cont; public: typedef boost::on_discover_vertex event_filter; path_history_visitor(continuation& cont) : cont(cont) {} template<typename Vertex, typename Graph> void operator()(Vertex v, const Graph&) { std::cout << Names[v] << ' '; cont.suspend(); } };
  • 23. int main() { const Graph g = make_graph(); const Vertex from = S; continuation cont = [&](continuation& cont) { path_history_visitor vis(cont); boost::dijkstra_shortest_paths(g, from, boost::visitor(boost::make_dijkstra_visitor(vis))); }; while (!cont.is_complete()) { cont.resume(); } } ポイントはラムダ式とEvent Visitor。 Event Visitorは、アルゴリズムの中断ポイントを定義するのに使用できる。 ラムダは、既存のプログラムをBoost.Contextで使用できるコンテキストに変換するのに使 用できる。
  • 24. その他 • ambを実装した https://github.com/faithandbrave/Keizoku/tree/master/amb • Boost.CoroutineがBoost.Contextで書き直されるらしい 「Boost.Coroutineを用いてステートマシンを解決する – C++Now! 2012」 https://sites.google.com/site/boostjp/cppnow/2012#coroutine • Scala設計者のMartin Odersky先生が「Observerパターンやめて継続使お うぜ」という論文を書いてる 「Deprecating the Observer Pattern」 http://lampwww.epfl.ch/~imaier/pub/DeprecatingObserversTR2010.pdf