// Copyright Evgeny Panasyuk 2013. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // e-mail: E?????[dot]P???????[at]gmail.??? // Full emulation of await feature from C# language in C++ based on Stackful Coroutines from // Boost.Coroutine library. // This proof-of-concept shows that exact syntax of await feature can be emulated with help of // Stackful Coroutines, demonstrating that it is superior mechanism. // Main aim of this proof-of-concept is to draw attention to Stackful Coroutines. #define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #define BOOST_THREAD_PROVIDES_FUTURE #define BOOST_RESULT_OF_USE_DECLTYPE #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace boost; // ___________________________________________________________ // template class concurrent_queue { queue q; mutex m; condition_variable c; public: template void push(U &&u) { lock_guard l(m); q.push( forward(u) ); c.notify_one(); } void pop(T &result) { unique_lock u(m); c.wait(u, [&]{return !q.empty();} ); result = move_if_noexcept(q.front()); q.pop(); } }; typedef std::function Task; concurrent_queue main_tasks; auto finished = false; void reschedule() { this_thread::sleep_for(chrono::milliseconds( rand() % 2000 )); } // ___________________________________________________________ // typedef coroutines::coroutine::pull_type coro_pull; typedef coroutines::coroutine::push_type coro_push; struct CurrentCoro { std::shared_ptr coro; coro_push *caller; }; /*should be thread_local*/ stack coro_stack; template auto asynchronous(F f) -> future { typedef promise CoroPromise; CoroPromise coro_promise; auto coro_future = coro_promise.get_future(); // It is possible to avoid shared_ptr and use move-semantic, // but it would require to refuse use of std::function (it requires CopyConstructable), // and would lead to further complication and is unjustified // for purposes of this proof-of-concept CurrentCoro current_coro = { make_shared(std::bind( [f](CoroPromise &coro_promise, coro_push &caller) { caller(); coro_stack.top().caller = &caller; coro_promise.set_value( f() ); }, std::move(coro_promise), placeholders::_1 )) }; coro_stack.push( std::move(current_coro) ); (*coro_stack.top().coro)(); coro_stack.pop(); #ifdef _MSC_VER return std::move( coro_future ); #else return coro_future; #endif } struct Awaiter { template auto operator*(Future &&ft) -> decltype(ft.get()) { typedef decltype(ft.get()) Result; auto &¤t_coro = coro_stack.top(); auto result = ft.then([current_coro](Future &ft) -> Result { main_tasks.push([current_coro] { coro_stack.push(std::move(current_coro)); (*coro_stack.top().coro)(); coro_stack.pop(); }); return ft.get(); }); (*coro_stack.top().caller)(); return result.get(); } }; #define await Awaiter()* // ___________________________________________________________ // void async_user_handler(); int main() { srand(time(0)); // Custom scheduling is not required - can be integrated // to other systems transparently main_tasks.push([] { asynchronous([] { return async_user_handler(), finished = true; }); }); Task task; while(!finished) { main_tasks.pop(task); task(); } } // __________________________________________________________________ // int bar(int i) { // await is not limited by "one level" as in C# auto result = await async([i]{ return reschedule(), i*100; }); return result + i*10; } int foo(int i) { cout << i << ":\tbegin" << endl; cout << await async([i]{ return reschedule(), i*10; }) << ":\tbody" << endl; cout << bar(i) << ":\tend" << endl; return i*1000; } void async_user_handler() { vector> fs; for(auto i=0; i!=5; ++i) fs.push_back( asynchronous([i]{ return foo(i+1); }) ); for(auto &&f : fs) cout << await f << ":\tafter end" << endl; }