Concurrency TS

Review for N4399:
“Working Draft, Technical Specification
for C++ Extensions for Concurrency”
稲葉 一浩 ([email protected])
SC22/C++WG May 2015
参考資料
• レポジトリ
– https://github.com/cplusplus/concurrency_ts
• 関連 Proposal
–
–
–
–
...  N3784 : “Improvements to std::future<T> and Related APIs”
...  N3785 : “Executors and Schedulers”
...  N4392 : “C++ Latches and Barriers”
...  N4162 : “Atomic Smart Pointers”
• TS Draft
–
–
–
–
–
–
N3970 : May 2014 (≒N3784 + N3785)
Editor note: N3971, Comments: N4032, N4048
N4107 : Jul 2014 (-= N3785)
Editor note: N4108, Revisions: N4123, N4313
N4399 : Apr 2015 (+= N4204, N4162)
Editor Note: N4400
N4399 の内容
• 3本立て
1. Improvements to std::future<T> and Related APIs
2. Latches and Barriers
3. Atomic Smart Pointers
おさらい: futureとは
• 「どこかで実行された計算の結果がそのうち
入る」行き先を表す
std::future<int> ft = std::async([](){
// 非同期にこの関数を実行
return 42;
});
if (ft.wait_for(std::chrono::seconds(1))
== std::future_status::ready) {
int v = ft.get(); // 42
}
1. Improvements to std::future<T>
• 以下のメンバ関数を追加
– future(future<future<R>>&& rhs);
– see below then(F&& func);
– bool is_ready() const noexcept;
• 以下の関数を追加
–
–
–
–
–
–
see below when_all(InputIterator begin, InputIterator end);
see below when_all(Futures&&... futures);
see below when_any(InputIterator begin, InputIterator end);
see below when_any(Futures&&... futures);
future<decay_t<T>> make_ready_future(T&& value);
future<T> make_execeptional_future(exception_ptr ex);
複数のfuture(やfutureを作る計算)を組み合わせて
新しいfutureを作るユーティリティ関数群
1. Improvements to std::future<T>
• 以下のメンバ関数を追加
– future(future<future<R>>&& rhs);
– see below then(F&& func);
– bool is_ready() const noexcept;
• 以下の関数を追加
–
–
–
–
–
–
C++11 ドラフト段階では一度
提案されていた関数。復活
see below when_all(InputIterator begin, InputIterator end);
see below when_all(Futures&&... futures);
see below when_any(InputIterator begin, InputIterator end);
see below when_any(Futures&&... futures);
future<decay_t<T>> make_ready_future(T&& value);
future<T> make_execeptional_future(exception_ptr ex);
1. Improvements to std::future<T>
• 以下のメンバ関数を追加
– future(future<future<R>>&& rhs);
– see below then(F&& func);
– bool is_ready() const noexcept;
• 以下の関数を追加
–
–
–
–
–
–
see below when_all(InputIterator begin, InputIterator end);
see below when_all(Futures&&... futures);
see below when_any(InputIterator begin, InputIterator end);
see below when_any(Futures&&... futures);
future<decay_t<T>> make_ready_future(T&& value);
future<T> make_execeptional_future(exception_ptr ex);
1. Improvements to std::future<T>
• future<T> ft = ...; ft.then(func);
• 「ft の値が取り出し可能になったら func(ft) を実行してその値を
返す future」 を即座に返す
• func は future<T> を引数にとる。
– T ではない。
– Haskell の Monad (引数型はT) や ECMAScript6 の Promise (引数型はT、
Exception時のためにもう一つ関数オブジェクトを渡す) とは型が違う
• funcの返値型が future<R> なら then の型も future<R>
• funcの返値型がそれ以外の R なら then の型は future<R>
– (前者が “implicit unwrapping” と呼ばれる特殊規則。
future<future<R>> にはならない)
1. Improvements to std::future<T>
• future<T> ft = make_ready_future(v);
• 「値 v が既に取り出し可能な状態になっている future」
– T を future<T> に変換する
• future(future<future<R>>&& rhs);
– future<future<T>> を future<T> に変換する
• rhsとrhs.get()がreadyならready。get()はrhs.get()の結果
になる
1. Improvements to std::future<T>
• 以下のメンバ関数を追加
– future(future<future<R>>&& rhs);
– see below then(F&& func);
– bool is_ready() const noexcept;
• 以下の関数を追加
–
–
–
–
–
–
see below when_all(InputIterator begin, InputIterator end);
see below when_all(Futures&&... futures);
see below when_any(InputIterator begin, InputIterator end);
see below when_any(Futures&&... futures);
future<decay_t<T>> make_ready_future(T&& value);
future<T> make_execeptional_future(exception_ptr ex);
1. Improvements to std::future<T>
• future<vector< typename
iterator_traits<InputIterator>::value_type >>
when_all(InputIterator, InputIterator);
• future<tuple<decay_t<Futures>...>>
when_all(Futures&&...);
– 「引数に渡した全てのfutureがreadyになったら
readyになるfuture」を返す。返値はfutureの
vector/tupleのfuture。
• readyになったら引数のfutureがmoveされる
1. Improvements to std::future<T>
• template<class Sequence>
struct when_any_result {
size_t index; Sequence futures; };
• future<when_any_result<vector<typename
iterator_traits<InputIterator>::value_type>>>
when_any(InputIterator, InputIterator);
• future<when_any_result<tuple<decay_t<Futures>...>>>
when_any(Futures&&... futures);
– 引数どれか一つ以上がreadyになったらreadyになるfutureを返
す
• 引数が0個なら即座にreadyとなる
– 返値は「引数全部のSequenceと発火したindex」という意図だと
思われるが、現在のdraftではindexについて何も触れられてい
ない (ミス?)
気になったところ
• then や when_all や when_any に引数として
渡したfutureは、関数から返った時点で無効
になっている
– “Postcondition” の項を見るとわかる
– “Effects” の項を見ると、新しく作った future が
ready になった後に move が行われることは書か
れているが、関数呼び出し時については言及さ
れていない
2. Latches and Barriers
• スレッド間同期プリミティブ
– カウンタの初期値 n
• 最初に同期ポイントに入った n 個のスレッドが “participating thread ” となる
– 各スレッドは基本的に 「1 減らして待つ」 ことができる
– カウンタが 0 になったら待ちスレッド全て実行再開
• latch
– Single-use (一度カウンタが 0 になったら destruct しかできない)
• barrier
– Single-use ではない (カウンタが0になったあと戻る)
– arrive_and_drop() という操作で participating thread から抜けられる
• flex_barrier
– コンストラクタに関数オブジェクト (ptrdiff_t ()) を指定できる。
– カウンタ 0 になった時に呼ばれる。
– -1 を返すと barrier と同じ動作。それ以外の値を返すと新しい participating
thread 集合のサイズとして扱われる
class latch { public:
explicit latch(ptrdiff_t count);
void
void
bool
void
count_down_and_wait();
count_down(ptrdiff_t n);
is_ready() const noexcept;
wait() const;
};
少し前までの
proposal では
arrive_and_wait()
だった。
count_down(n)
に合わせて改名?
class barrier { public:
explicit barrier(ptrdiff_t num_threads);
void arrive_and_wait();
void arrive_and_drop();
};
class flex_barrier { public:
template <class F>
flex_barrier(ptrdiff_t num_threads, F completion);
explicit flex_barrier(ptrdiff_t num_threads);
void arrive_and_wait();
void arrive_and_drop();
};
3. Atomic Smart Pointers
• atomic_shared_ptr<T>
• atomic_weak_ptr<T>
– shared_ptr<T> と weak_ptr<T> の atomic版
• “The behavior of all operations is as specified
in C++14 §29.6.5, unless stated otherwise.”
• “When any operation on an atomic_shared_ptr
or atomic_weak_ptr causes an object to be destroyed
or memory to be deallocated, that destruction or
deallocation shall be sequenced after the changes to
the atomic object's state.”
– atomic_unique_ptr<T> は無い(なぜ?)