Facebookは先頃,Folly Futureという,既存のstd::futureを機能拡張するC++11ライブラリを導入した。
Futureは並列操作の同期に使用される構成要素である。最初は値の分からない非同期演算の結果を参照する,読み取り専用のプロキシオブジェクトがFutureだ。まだ完了(fulfill)していないFutureの値をクライアントが読み出そうとすると,そのクライアントはブロックされる。ひとつのFutureは通常,ひとつのPromiseに関連付けられている。Futureの値への書き込みアクセスを提供するのがPromiseだ。
以下のコードで示すような非同期操作では,ブロックされることなく,読み取り専用のFutureをすぐに返すことが可能だ。
#include <folly/futures/Future.h>
using folly::Future;
Future<Output> asyncOperation(Input);
Future<Output> f = asyncOperation(input);
asyncOperationは非同期呼び出しのラッパである。 クライアントは次にFutureにアクセスして,関連するPromiseがisReady()メソッドを使って完了しているかチェックした上で,value()を使ってその値を取得する。
Futureはその関連するPromiseから生成されて,setValue()かsetWith()のいずれかを使って値を設定することができる。
using folly::Promise;
Future<double> getEnergy(int year) {
auto promise = make_shared<Promise<double>>();
std::thread([=]{
promise->setWith(std::bind(getEnergySync, year));
}).detach();
return promise->getFuture();
}
Folly Futuresが本領を発揮するのはFuture::thenメソッドを使うときだ。コールバックの連鎖が簡単に実現可能であるため,コールバック地獄を防止できる。チェーンがどのように動作するのかを以下に示す。
Future<OutputA> futureA(Output);
Future<OutputB> futureB(OutputA);
Future<OutputC> futureC(OutputB);
OutputD d(OutputC) {
if (somethingExceptional) throw anException;
return OutputD();
}
Future<double> fut =
fooFuture(input)
.then(futureA)
.then(futureB)
.then(futureC)
.then(d)
.then([](OutputD outputD) { // lambdas are ok too
return outputD * M_PI;
});
Folly Futuresの提供する,もうひとつの強力なビルディングブロックがcollectメソッドだ。Futureのコレクションを,すべてのFutureの完了によって完了状態になる単一のFutureとして扱うことができる。collertと同じように,次の機能も提供されている。
collectAny: 入力のFutureのいずれかの完了によって完了する。collectN: N個のFutureが完了するまで待機する。map: Futureのコレクションと関数を設定し,各入力Futureのthen()に対してその関数をコールする。Futureの別の配列を返す。reduce: Futureのコレクション以外に2つの引数関数(換算値と換算シーケンスの次の値)を取って,それぞれのFutureにその関数を適用する。最後に,Folly Futuresでは,コールバックの実行時に実行コンテキストの概念もサポートされている。executorオブジェクトをthenに渡して,そのコールバックをexecutor経由で行うようなことも可能だ。
struct Executor {
using Func = std::function<void()>;
virtual void add(Func) = 0;
};
a(input).then(executor, b);
Folly Futureの詳しい情報は,資料で見ることができる。