稲葉 一浩 (kinaba@google.com) SC22/C++WG May 2015 Review for N4399: “Working Draft, Technical Specification for C++ Extensions for Concurrency” 稲葉 一浩 (kinaba@google.com) SC22/C++WG May 2015
参考資料 レポジトリ 関連 Proposal TS Draft 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本立て Improvements to std::future<T> and Related APIs Latches and Barriers 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; 以下の関数を追加 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); C++11 ドラフト段階では一度 提案されていた関数。復活
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 スレッド間同期プリミティブ latch barrier flex_barrier 最初に同期ポイントに入った 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 count_down_and_wait(); void count_down(ptrdiff_t n); bool is_ready() const noexcept; void 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> は無い(なぜ?)