├── await ├── await.cpp ├── await.h ├── boost_await.cpp ├── boost_await.h ├── coroutine.cpp ├── coroutine.h ├── function.hpp ├── includeOnly.cpp ├── stack_swap.cpp ├── stack_swap.h ├── stack_swap_asm.asm ├── then_future.cpp └── then_future.h ├── build_times.py ├── compile_time.pro ├── dunique_ptr.hpp ├── flat_map.cpp ├── flat_map.hpp └── main.cpp /await/await.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #include "await.h" 28 | 29 | AwaitTasksToFinish await_tasks_to_finish; 30 | 31 | namespace detail 32 | { 33 | thread_local std::stack > ActiveCoroutine::active_coroutines; 34 | } 35 | 36 | 37 | #ifndef DISABLE_GTEST 38 | #include 39 | namespace 40 | { 41 | TEST(resumable, no_await) 42 | { 43 | ASSERT_EQ(5, resumable([]{ return 5; }).get()); 44 | } 45 | TEST(resumable, async) 46 | { 47 | then_future background_future = custom_async([]{ return 5; }); 48 | auto future = resumable([&background_future] 49 | { 50 | return await background_future; 51 | }); 52 | await_tasks_to_finish.run_single_task_blocking(); 53 | ASSERT_EQ(5, future.get()); 54 | } 55 | TEST(resumable, await_twice) 56 | { 57 | then_future future_a = custom_async([]{ return 5; }); 58 | then_future future_b = custom_async([]{ return 5; }); 59 | bool finished = false; 60 | auto future = resumable([&finished, &future_a, &future_b] 61 | { 62 | int result = await future_a + await future_b; 63 | finished = true; 64 | return result; 65 | }); 66 | while (!finished) await_tasks_to_finish.run_single_task_blocking(); 67 | ASSERT_EQ(10, future.get()); 68 | } 69 | struct immediate_future 70 | { 71 | template 72 | immediate_future(Func && func) 73 | : result(func()) 74 | { 75 | } 76 | template 77 | immediate_future then(Func && func) 78 | { 79 | return immediate_future([&]{ return func(*this); }); 80 | } 81 | int get() const 82 | { 83 | return result; 84 | } 85 | 86 | private: 87 | int result; 88 | }; 89 | TEST(resumable, finish_immediately) 90 | { 91 | auto future = resumable([] 92 | { 93 | return await immediate_future([]{ return 5; }); 94 | }); 95 | await_tasks_to_finish.run_single_task_blocking(); 96 | ASSERT_EQ(5, future.get()); 97 | } 98 | TEST(resumable, operator_precedence) 99 | { 100 | bool finished = false; 101 | auto future = resumable([&finished] 102 | { 103 | int result = await immediate_future([]{ return 5; }) * await immediate_future([]{ return 5; }); 104 | finished = true; 105 | return result; 106 | }); 107 | while (!finished) await_tasks_to_finish.run_single_task_blocking(); 108 | ASSERT_EQ(25, future.get()); 109 | } 110 | 111 | struct wait_for_other_thread 112 | { 113 | wait_for_other_thread() 114 | : promise(), future(promise.get_future()) 115 | { 116 | } 117 | void signal_ready() 118 | { 119 | promise.set_value(); 120 | } 121 | void wait() 122 | { 123 | future.wait(); 124 | } 125 | 126 | private: 127 | std::promise promise; 128 | std::future future; 129 | }; 130 | 131 | struct bad_timing_future 132 | { 133 | struct background_thread_func 134 | { 135 | void operator()() const 136 | { 137 | a->wait(); 138 | await_tasks_to_finish.finish_all(); 139 | b->signal_ready(); 140 | } 141 | 142 | std::shared_ptr a; 143 | std::shared_ptr b; 144 | }; 145 | 146 | bad_timing_future(func::movable_function func) 147 | : result(func()), a(std::make_shared()), b(std::make_shared()) 148 | , background_thread(background_thread_func{a, b}) 149 | { 150 | } 151 | bad_timing_future(bad_timing_future &&) = default; 152 | bad_timing_future & operator=(bad_timing_future &&) = default; 153 | ~bad_timing_future() 154 | { 155 | if (background_thread.joinable()) 156 | background_thread.join(); 157 | } 158 | 159 | template 160 | bad_timing_future then(Func && func) 161 | { 162 | return bad_timing_future([&]{ return func(*this); }); 163 | } 164 | int get() 165 | { 166 | // this will cause await_tasks_to_finish.finish_all() to run. this would break 167 | // the current coroutine if I didn't add it in after_yield 168 | a->signal_ready(); 169 | b->wait(); 170 | return result; 171 | } 172 | 173 | private: 174 | int result; 175 | std::shared_ptr a; 176 | std::shared_ptr b; 177 | std::thread background_thread; 178 | }; 179 | 180 | TEST(resumable, bad_timing) 181 | { 182 | auto future = resumable([] 183 | { 184 | return await bad_timing_future([]{ return 5; }); 185 | }); 186 | await_tasks_to_finish.run_single_task_blocking(); 187 | ASSERT_EQ(5, future.get()); 188 | } 189 | TEST(resumable, twice_bad_timing) 190 | { 191 | bool finished = false; 192 | auto future = resumable([&finished] 193 | { 194 | int result = await bad_timing_future([]{ return 5; }) + await bad_timing_future([]{ return 5; }); 195 | finished = true; 196 | return result; 197 | }); 198 | while (!finished) await_tasks_to_finish.run_single_task_blocking(); 199 | ASSERT_EQ(10, future.get()); 200 | } 201 | TEST(resumable, exception) 202 | { 203 | auto future = resumable([] 204 | { 205 | throw 5; 206 | }); 207 | ASSERT_THROW(future.get(), int); 208 | bool finished = false; 209 | future = resumable([&finished] 210 | { 211 | await custom_async([]{}); 212 | finished = true; 213 | throw 5; 214 | }); 215 | while (!finished) await_tasks_to_finish.run_single_task_blocking(); 216 | ASSERT_THROW(future.get(), int); 217 | } 218 | TEST(resumable, await_exception) 219 | { 220 | bool finished = false; 221 | then_future future = resumable([] 222 | { 223 | ASSERT_THROW(await custom_async([]{ throw 5; }), int); 224 | await custom_async([]{ throw 6.0f; }); 225 | }).then([&finished](then_future & future) 226 | { 227 | finished = true; 228 | future.get(); 229 | }); 230 | while (!finished) await_tasks_to_finish.run_single_task_blocking(); 231 | ASSERT_THROW(future.get(), float); 232 | } 233 | 234 | struct MovableFunctor 235 | { 236 | MovableFunctor(int & finished, int num_iterations) 237 | : finished(finished), async_count(new int(num_iterations)) 238 | { 239 | } 240 | 241 | void operator()() 242 | { 243 | if (*async_count) 244 | { 245 | --*async_count; 246 | await resumable(std::move(*this)); 247 | ++finished; 248 | } 249 | } 250 | 251 | int & finished; 252 | std::unique_ptr async_count; 253 | }; 254 | static_assert(!std::is_copy_constructible::value && !std::is_copy_assignable::value, "can't be copy constructible for my test below"); 255 | // this test is mainly here to guarantee that my code will compile with 256 | // functors that are movable only 257 | TEST(resumable, movable_functor) 258 | { 259 | int finished = 0; 260 | int num_iterations = 5; 261 | then_future future = resumable(MovableFunctor(finished, num_iterations)); 262 | while (finished != num_iterations) await_tasks_to_finish.run_single_task_blocking(); 263 | future.get(); 264 | } 265 | TEST(resumable, await_or_block) 266 | { 267 | ASSERT_EQ(5, await_or_block(custom_async([]{ return 5; }))); 268 | then_future future = resumable([]{ return await_or_block(custom_async([]{ return 5; })); }); 269 | await_tasks_to_finish.run_single_task_blocking(); 270 | ASSERT_EQ(5, future.get()); 271 | } 272 | } 273 | 274 | #endif 275 | -------------------------------------------------------------------------------- /await/await.h: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #pragma once 28 | 29 | #include "coroutine.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "then_future.h" 37 | 38 | struct AwaitTasksToFinish 39 | { 40 | void add(func::movable_function func) 41 | { 42 | { 43 | std::lock_guard lock(mutex); 44 | tasks_to_finish.push(std::move(func)); 45 | } 46 | waiter.notify_one(); 47 | } 48 | void finish_all() 49 | { 50 | while (run_single_task()); 51 | } 52 | // will return true if it actually ran a task 53 | // will return false if no task was available 54 | bool run_single_task() 55 | { 56 | func::movable_function task = get_next_task(); 57 | if (!task) return false; 58 | task(); 59 | return true; 60 | } 61 | // will return after it has run a task 62 | void run_single_task_blocking() 63 | { 64 | while (!run_single_task()) wait_for_task(); 65 | } 66 | 67 | private: 68 | func::movable_function get_next_task() 69 | { 70 | std::lock_guard lock(mutex); 71 | if (tasks_to_finish.empty()) return {}; 72 | func::movable_function result = std::move(tasks_to_finish.front()); 73 | tasks_to_finish.pop(); 74 | return result; 75 | } 76 | void wait_for_task() const 77 | { 78 | std::unique_lock lock(mutex); 79 | waiter.wait(lock, [this]{ return !tasks_to_finish.empty(); }); 80 | } 81 | 82 | mutable std::condition_variable waiter; 83 | mutable std::mutex mutex; 84 | // possible to optimize here by using a lockless queue 85 | std::queue > tasks_to_finish; 86 | }; 87 | 88 | // coroutines that have used await will add themselves to this list when they 89 | // are ready to continue running. that means that you are responsible for 90 | // continuing these tasks. the benefit of that is that this will work with your 91 | // existing threading framework. just regularly call 92 | // await_tasks_to_finish.run_single_task() 93 | extern AwaitTasksToFinish await_tasks_to_finish; 94 | 95 | struct Awaiter; 96 | namespace detail 97 | { 98 | struct ActiveCoroutine 99 | { 100 | friend struct ::Awaiter; 101 | template 102 | ActiveCoroutine(then_promise promise, Func && func) 103 | : heap_stuff(std::make_shared(std::move(promise), std::forward(func))) 104 | { 105 | } 106 | 107 | static ActiveCoroutine * get_current_coroutine() 108 | { 109 | if (active_coroutines.empty()) return nullptr; 110 | else return &active_coroutines.top(); 111 | } 112 | 113 | void operator()() 114 | { 115 | active_coroutines.push(*this); 116 | heap_stuff->run_again.reset(); 117 | heap_stuff->coroutine(); 118 | active_coroutines.pop(); 119 | if (heap_stuff->run_again.signal()) 120 | { 121 | await_tasks_to_finish.add(*this); 122 | } 123 | } 124 | 125 | void yield() 126 | { 127 | heap_stuff->yielder->yield(); 128 | } 129 | 130 | private: 131 | struct HeapStuff 132 | { 133 | template 134 | HeapStuff(then_promise && promise, Func && func) 135 | : coroutine(CoroStartFunction::type>(std::move(promise), std::forward(func), yielder)) 136 | { 137 | } 138 | 139 | coro::coroutine::self * yielder = nullptr; 140 | coro::coroutine coroutine; 141 | two_thread_gate run_again; 142 | 143 | template 144 | struct CoroStartFunction 145 | { 146 | CoroStartFunction(then_promise && promise, Func function, coro::coroutine::self *& yielder) 147 | : yielder(yielder), promise(std::move(promise)), function(std::move(function)) 148 | { 149 | } 150 | 151 | void operator()(coro::coroutine::self & self) 152 | { 153 | yielder = &self; 154 | detail::set_promise_value(this->promise, this->function); 155 | } 156 | 157 | coro::coroutine::self *& yielder; 158 | then_promise promise; 159 | Func function; 160 | }; 161 | }; 162 | 163 | std::shared_ptr heap_stuff; 164 | static thread_local std::stack > active_coroutines; 165 | }; 166 | } 167 | 168 | struct Awaiter 169 | { 170 | template 171 | auto operator->*(Future && f) const -> decltype(f.get()) 172 | { 173 | detail::ActiveCoroutine * active_coroutine = detail::ActiveCoroutine::get_current_coroutine(); 174 | if (!active_coroutine) 175 | { 176 | // not sure what to do in this case. if you call await from outside of a coroutine I can't 177 | // simply switch out of the current stack. there is nothing to switch to. instead I could 178 | // either crash or block until the result is available. since it's probably an error if 179 | // await blocks, I decided to crash. if you want to block instead you can use await_or_block() 180 | throw std::runtime_error("you can only use 'await' inside of functions called from resumable(). the reason is that I have to switch out of the current context when you call await and I don't know what to switch to from the main context"); 181 | return f.get(); 182 | } 183 | detail::ActiveCoroutine coro = *active_coroutine; 184 | // here is the idea: in the future's .then() callback 185 | // we add ourself to the await_tasks_to_finish list 186 | auto finish = f.then([coro](Future & f) mutable -> decltype(f.get()) 187 | { 188 | if (coro.heap_stuff->run_again.signal()) 189 | { 190 | await_tasks_to_finish.add(std::move(coro)); 191 | } 192 | return f.get(); 193 | }); 194 | // now we yield, so that when the above callback triggers, 195 | // we'll continue here 196 | coro.yield(); 197 | return finish.get(); 198 | } 199 | }; 200 | 201 | #define await ::Awaiter()->* 202 | 203 | // will return true if it is valid to use the await macro in the current 204 | // context. it is only valid to use the await macro in functions that have been 205 | // started using the resumable() function below 206 | inline bool can_await() 207 | { 208 | return detail::ActiveCoroutine::get_current_coroutine(); 209 | } 210 | 211 | template 212 | inline auto await_or_block(Future && future) -> decltype(future.get()) 213 | { 214 | return can_await() ? await future : future.get(); 215 | } 216 | 217 | template 218 | auto resumable(Func && func) -> then_future 219 | { 220 | typedef decltype(func()) result_type; 221 | then_promise promise; 222 | then_future future = promise.get_future(); 223 | // create a coroutine with the function and start it immediately 224 | // we don't need to store it because if it never calls await, 225 | // it will finish immediately. if it does call await, it will add 226 | // itself to the await_tasks_to_finish list. so in either case its 227 | // lifetime is taken care of 228 | detail::ActiveCoroutine(std::move(promise), std::forward(func))(); 229 | return future; 230 | } 231 | -------------------------------------------------------------------------------- /await/boost_await.cpp: -------------------------------------------------------------------------------- 1 | //#define USE_BOOST_AWAIT 2 | #ifdef USE_BOOST_AWAIT 3 | # include "boost_await.h" 4 | # define async(...) boost::async(__VA_ARGS__) 5 | concurrent_queue main_tasks; 6 | thread_local std::stack coro_stack; 7 | #else 8 | # include "await.h" 9 | # define asynchronous(...) resumable(__VA_ARGS__) 10 | # define async(...) custom_async(__VA_ARGS__) 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | auto finished = false; 21 | 22 | void reschedule() 23 | { 24 | std::this_thread::sleep_for(std::chrono::milliseconds( rand() % 2000 )); 25 | } 26 | 27 | // ___________________________________________________________ // 28 | 29 | void async_user_handler(); 30 | 31 | #include 32 | TEST(boost_await, main) 33 | { 34 | srand(time(0)); 35 | 36 | // Custom scheduling is not required - can be integrated 37 | // to other systems transparently 38 | #ifdef USE_BOOST_AWAIT 39 | main_tasks.push( 40 | #else 41 | await_tasks_to_finish.add( 42 | #endif 43 | [] 44 | { 45 | asynchronous([] 46 | { 47 | return async_user_handler(), 48 | finished = true; 49 | }); 50 | }); 51 | 52 | #ifdef USE_BOOST_AWAIT 53 | Task task; 54 | while(!finished) 55 | { 56 | main_tasks.pop(task); 57 | task(); 58 | } 59 | #else 60 | while (!finished) await_tasks_to_finish.run_single_task_blocking(); 61 | #endif 62 | } 63 | 64 | // __________________________________________________________________ // 65 | 66 | int bar(int i) 67 | { 68 | // await is not limited by "one level" as in C# 69 | auto result = await async([i]{ return reschedule(), i*100; }); 70 | return result + i*10; 71 | } 72 | 73 | int foo(int i) 74 | { 75 | std::cout << i << ":\tbegin" << std::endl; 76 | std::cout << await async([i]{ return reschedule(), i*10; }) << ":\tbody" << std::endl; 77 | std::cout << bar(i) << ":\tend" << std::endl; 78 | return i*1000; 79 | } 80 | 81 | void async_user_handler() 82 | { 83 | #ifdef USE_BOOST_AWAIT 84 | typedef boost::future future_type; 85 | #else 86 | typedef then_future future_type; 87 | #endif 88 | std::vector fs; 89 | 90 | for(auto i=0; i!=5; ++i) 91 | fs.push_back( asynchronous([i]{ return foo(i+1); }) ); 92 | 93 | for(auto && f : fs) 94 | std::cout << await f << ":\tafter end" << std::endl; 95 | } 96 | -------------------------------------------------------------------------------- /await/boost_await.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Copyright Evgeny Panasyuk 2013. 4 | /* 5 | Boost Software License - Version 1.0 - August 17th, 2003 6 | 7 | Permission is hereby granted, free of charge, to any person or organization 8 | obtaining a copy of the software and accompanying documentation covered by 9 | this license (the "Software") to use, reproduce, display, distribute, 10 | execute, and transmit the Software, and to prepare derivative works of the 11 | Software, and to permit third-parties to whom the Software is furnished to 12 | do so, all subject to the following: 13 | 14 | The copyright notices in the Software and this entire statement, including 15 | the above license grant, this restriction and the following disclaimer, 16 | must be included in all copies of the Software, in whole or in part, and 17 | all derivative works of the Software, unless such copies or derivative 18 | works are solely in the form of machine-executable object code generated by 19 | a source language processor. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 24 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 25 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 26 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. 28 | */ 29 | // e-mail: E?????[dot]P???????[at]gmail.??? 30 | 31 | // Full emulation of await feature from C# language in C++ based on Stackful Coroutines from 32 | // Boost.Coroutine library. 33 | // This proof-of-concept shows that exact syntax of await feature can be emulated with help of 34 | // Stackful Coroutines, demonstrating that it is superior mechanism. 35 | // Main aim of this proof-of-concept is to draw attention to Stackful Coroutines. 36 | 37 | #define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION 38 | #define BOOST_THREAD_PROVIDES_FUTURE 39 | #define BOOST_RESULT_OF_USE_DECLTYPE 40 | 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | // ___________________________________________________________ // 51 | 52 | template 53 | class concurrent_queue 54 | { 55 | std::queue q; 56 | boost::mutex m; 57 | boost::condition_variable c; 58 | public: 59 | template 60 | void push(U &&u) 61 | { 62 | boost::lock_guard l(m); 63 | q.push( std::forward(u) ); 64 | c.notify_one(); 65 | } 66 | void pop(T &result) 67 | { 68 | boost::unique_lock u(m); 69 | c.wait(u, [&]{return !q.empty();} ); 70 | result = std::move_if_noexcept(q.front()); 71 | q.pop(); 72 | } 73 | }; 74 | 75 | typedef std::function Task; 76 | extern concurrent_queue main_tasks; 77 | // ___________________________________________________________ // 78 | 79 | typedef boost::coroutines::coroutine Coro; 80 | struct CurrentCoro 81 | { 82 | std::shared_ptr coro; 83 | Coro::caller_type *caller; 84 | }; 85 | extern thread_local std::stack coro_stack; 86 | 87 | template 88 | auto asynchronous(F f) -> boost::future 89 | { 90 | typedef boost::promise CoroPromise; 91 | 92 | CoroPromise coro_promise; 93 | auto coro_future = coro_promise.get_future(); 94 | 95 | // It is possible to avoid shared_ptr and use move-semantic, 96 | // but it would require to refuse use of std::function (it requires CopyConstructable), 97 | // and would lead to further complication and is unjustified 98 | // for purposes of this proof-of-concept 99 | CurrentCoro current_coro = 100 | { 101 | std::make_shared(std::bind( [f](CoroPromise &coro_promise, Coro::caller_type &caller) 102 | { 103 | caller(); 104 | coro_stack.top().caller = &caller; 105 | coro_promise.set_value( f() ); 106 | }, std::move(coro_promise), std::placeholders::_1 )), nullptr 107 | }; 108 | 109 | coro_stack.push( std::move(current_coro) ); 110 | (*coro_stack.top().coro)(); 111 | coro_stack.pop(); 112 | 113 | return coro_future; 114 | } 115 | 116 | struct Awaiter 117 | { 118 | template 119 | auto operator*(Future &&ft) -> decltype(ft.get()) 120 | { 121 | typedef decltype(ft.get()) Result; 122 | 123 | auto &¤t_coro = coro_stack.top(); 124 | auto result = ft.then([current_coro](Future &ft) -> Result 125 | { 126 | main_tasks.push([current_coro] 127 | { 128 | coro_stack.push(std::move(current_coro)); 129 | (*coro_stack.top().coro)(); 130 | coro_stack.pop(); 131 | }); 132 | return ft.get(); 133 | }); 134 | (*coro_stack.top().caller)(); 135 | return result.get(); 136 | } 137 | }; 138 | #define await Awaiter()* 139 | 140 | -------------------------------------------------------------------------------- /await/coroutine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #include "coroutine.h" 28 | 29 | namespace coro 30 | { 31 | coroutine::self::self(coroutine & owner) 32 | : owner(&owner) 33 | { 34 | } 35 | coroutine::coroutine(coroutine_stack stack) 36 | : func() 37 | , stack(std::move(stack)) 38 | , stack_context(this->stack.get(), this->stack.size(), &coroutine_start, this) 39 | , run_state(UNINITIALIZED) 40 | { 41 | } 42 | coroutine::coroutine(func::movable_function func, coroutine_stack stack) 43 | : func(std::move(func)) 44 | , stack(std::move(stack)) 45 | , stack_context(this->stack.get(), this->stack.size(), &coroutine_start, this) 46 | , run_state(NOT_STARTED) 47 | { 48 | } 49 | coroutine::~coroutine() 50 | { 51 | CORO_ASSERT(run_state != RUNNING); 52 | } 53 | 54 | void coroutine::reset(func::movable_function func) 55 | { 56 | CORO_ASSERT(run_state != RUNNING); 57 | stack_context.reset(stack.get(), stack.size(), &coroutine_start, this); 58 | run_state = NOT_STARTED; 59 | this->func = std::move(func); 60 | } 61 | } 62 | 63 | 64 | #ifndef DISABLE_GTEST 65 | #include 66 | 67 | TEST(coroutine, simple) 68 | { 69 | using namespace coro; 70 | int called = 0; 71 | coroutine test([&called](coroutine::self & self) 72 | { 73 | ++called; 74 | self.yield(); 75 | ++called; 76 | }); 77 | EXPECT_TRUE(bool(test)); 78 | test(); 79 | EXPECT_EQ(1, called); 80 | EXPECT_TRUE(bool(test)); 81 | test(); 82 | EXPECT_EQ(2, called); 83 | EXPECT_FALSE(bool(test)); 84 | } 85 | 86 | TEST(coroutine, call_from_within) 87 | { 88 | using namespace coro; 89 | std::vector pushed; 90 | coroutine call_from_within_test([&pushed](coroutine::self & self) 91 | { 92 | coroutine inner([&pushed](coroutine::self & self) 93 | { 94 | for (int i = 0; i < 3; ++i) 95 | { 96 | pushed.push_back(1); 97 | self.yield(); 98 | } 99 | }); 100 | for (int i = 0; i < 3; ++i) 101 | { 102 | pushed.push_back(2); 103 | while (inner) 104 | { 105 | inner(); 106 | self.yield(); 107 | } 108 | } 109 | }); 110 | 111 | while (call_from_within_test) 112 | { 113 | call_from_within_test(); 114 | } 115 | ASSERT_EQ(6, pushed.size()); 116 | EXPECT_EQ(2, pushed[0]); 117 | EXPECT_EQ(1, pushed[1]); 118 | EXPECT_EQ(1, pushed[2]); 119 | EXPECT_EQ(1, pushed[3]); 120 | EXPECT_EQ(2, pushed[4]); 121 | EXPECT_EQ(2, pushed[5]); 122 | } 123 | 124 | TEST(coroutine, reset) 125 | { 126 | using namespace coro; 127 | coroutine empty; 128 | ASSERT_FALSE(bool(empty)); 129 | 130 | int count = 0; 131 | auto to_run = [&count](coroutine::self & self) 132 | { 133 | ++count; 134 | self.yield(); 135 | ++count; 136 | self.yield(); 137 | ++count; 138 | }; 139 | coroutine c(to_run); 140 | c(); 141 | c(); 142 | c(); 143 | c.reset(to_run); 144 | while (c) c(); 145 | ASSERT_EQ(6, count); 146 | } 147 | 148 | TEST(coroutine, never_called) 149 | { 150 | using namespace coro; 151 | coroutine([](coroutine::self &){}); 152 | } 153 | 154 | #ifndef CORO_NO_EXCEPTIONS 155 | TEST(coroutine, exception) 156 | { 157 | using namespace coro; 158 | coroutine thrower([](coroutine::self &) 159 | { 160 | throw 10; 161 | }); 162 | bool thrown = false; 163 | try 164 | { 165 | thrower(); 166 | } 167 | catch(int i) 168 | { 169 | EXPECT_EQ(10, i); 170 | thrown = true; 171 | } 172 | EXPECT_TRUE(thrown); 173 | } 174 | #endif 175 | 176 | #endif 177 | -------------------------------------------------------------------------------- /await/coroutine.h: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #pragma once 28 | 29 | #include "stack_swap.h" 30 | #include "function.hpp" 31 | #ifndef CORO_NO_EXCEPTIONS 32 | # include 33 | # include 34 | #endif 35 | #ifndef CORO_ASSERT 36 | # include 37 | # define CORO_ASSERT(cond) assert(cond) 38 | #endif 39 | 40 | #ifndef CORO_DEFAULT_STACK_SIZE 41 | # define CORO_DEFAULT_STACK_SIZE 64 * 1024 42 | #endif 43 | 44 | #ifndef CORO_ALLOCATE_BYTES 45 | # define CORO_ALLOCATE_BYTES(size) new unsigned char[size] 46 | #endif 47 | #ifndef CORO_FREE_BYTES 48 | # define CORO_FREE_BYTES(memory, size) delete[] memory 49 | #endif 50 | 51 | namespace coro 52 | { 53 | struct coroutine_stack 54 | { 55 | coroutine_stack(unsigned char * begin, unsigned char * end) 56 | : begin(begin), size_(end - begin), need_to_delete(false) 57 | { 58 | } 59 | explicit coroutine_stack(size_t size) 60 | : begin(CORO_ALLOCATE_BYTES(size)), size_(size), need_to_delete(true) 61 | { 62 | } 63 | coroutine_stack(coroutine_stack && other) 64 | : begin(other.begin), size_(other.size_), need_to_delete(other.need_to_delete) 65 | { 66 | other.begin = nullptr; 67 | other.size_ = 0; 68 | other.need_to_delete = false; 69 | } 70 | 71 | ~coroutine_stack() 72 | { 73 | if (need_to_delete) 74 | { 75 | CORO_FREE_BYTES(begin, size_); 76 | } 77 | } 78 | 79 | unsigned char * get() const 80 | { 81 | return begin; 82 | } 83 | 84 | size_t size() const 85 | { 86 | return size_; 87 | } 88 | 89 | private: 90 | coroutine_stack(const coroutine_stack &); // intentionally not implemented 91 | coroutine_stack & operator=(const coroutine_stack &); // intentionally not implemented 92 | coroutine_stack & operator=(coroutine_stack &&); // intentionally not implemented 93 | unsigned char * begin; 94 | size_t size_ : 63; 95 | bool need_to_delete : 1; 96 | }; 97 | static_assert(sizeof(coroutine_stack) == sizeof(void *) * 2, "expect size to be begin and end pointer"); 98 | 99 | struct coroutine 100 | { 101 | struct self 102 | { 103 | void yield() 104 | { 105 | owner->stack_context.switch_out_of(); 106 | } 107 | 108 | private: 109 | self(coroutine & owner); 110 | coroutine * owner; 111 | friend struct coroutine; 112 | }; 113 | 114 | explicit coroutine(coroutine_stack stack = coroutine_stack(CORO_DEFAULT_STACK_SIZE)); 115 | coroutine(func::movable_function func, coroutine_stack stack = coroutine_stack(CORO_DEFAULT_STACK_SIZE)); 116 | ~coroutine(); 117 | 118 | void reset(func::movable_function func); 119 | 120 | #ifdef CORO_NO_EXCEPTIONS 121 | void operator()() 122 | { 123 | if (!*this) 124 | { 125 | CORO_ASSERT(bool(*this)); 126 | return; 127 | } 128 | stack_context.switch_into(); // will continue here if yielded or returned 129 | } 130 | #else 131 | void operator()() 132 | { 133 | if (!*this) 134 | { 135 | CORO_ASSERT(bool(*this)); 136 | throw std::logic_error("You tried to call a coroutine in an invalid state"); 137 | } 138 | stack_context.switch_into(); // will continue here if yielded or returned 139 | if (exception) std::rethrow_exception(std::move(exception)); 140 | } 141 | #endif 142 | // will return true if you can call this coroutine 143 | operator bool() const 144 | { 145 | return run_state == NOT_STARTED || run_state == RUNNING; 146 | } 147 | 148 | private: 149 | // intentionally not implemented 150 | coroutine(const coroutine &); 151 | coroutine & operator=(const coroutine &); 152 | coroutine(coroutine &&); 153 | coroutine & operator=(coroutine &&); 154 | 155 | func::movable_function func; 156 | coroutine_stack stack; 157 | stack::stack_context stack_context; 158 | # ifndef CORO_NO_EXCEPTIONS 159 | std::exception_ptr exception; 160 | # endif 161 | enum RunState 162 | { 163 | NOT_STARTED, 164 | RUNNING, 165 | FINISHED, 166 | UNINITIALIZED 167 | } run_state; 168 | 169 | // this is the function that the coroutine will start off in 170 | #ifdef CORO_NO_EXCEPTIONS 171 | static void coroutine_start(void * ptr) 172 | { 173 | coroutine & this_ = *static_cast(ptr); 174 | this_.run_state = RUNNING; 175 | self self_(this_); 176 | this_.func(self_); 177 | this_.run_state = FINISHED; 178 | } 179 | #else 180 | static void coroutine_start(void * ptr) 181 | { 182 | coroutine & this_ = *static_cast(ptr); 183 | this_.run_state = RUNNING; 184 | try 185 | { 186 | self self_(this_); 187 | this_.func(self_); 188 | } 189 | catch(...) 190 | { 191 | this_.exception = std::current_exception(); 192 | } 193 | this_.run_state = FINISHED; 194 | } 195 | #endif 196 | }; 197 | 198 | } 199 | -------------------------------------------------------------------------------- /await/function.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | // despite that it would be nice if you give credit to Malte Skarupke 28 | 29 | 30 | #pragma once 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #ifdef _MSC_VER 39 | #define FUNC_NOEXCEPT 40 | #define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR) 41 | #else 42 | #define FUNC_NOEXCEPT noexcept 43 | #define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR) noexcept(detail::is_inplace_allocated::value) 44 | #endif 45 | #ifdef __GNUC__ 46 | #pragma GCC diagnostic push 47 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" 48 | #endif 49 | 50 | #define FUNC_MOVE(value) static_cast::type &&>(value) 51 | #define FUNC_FORWARD(type, value) static_cast(value) 52 | 53 | namespace func 54 | { 55 | #ifndef FUNC_NO_EXCEPTIONS 56 | struct bad_function_call : std::exception 57 | { 58 | const char * what() const FUNC_NOEXCEPT override 59 | { 60 | return "Bad function call"; 61 | } 62 | }; 63 | #endif 64 | 65 | template 66 | struct force_function_heap_allocation 67 | : std::false_type 68 | { 69 | }; 70 | 71 | template 72 | class movable_function; 73 | 74 | namespace detail 75 | { 76 | struct manager_storage_type; 77 | struct function_manager; 78 | struct functor_padding 79 | { 80 | protected: 81 | size_t padding_first; 82 | size_t padding_second; 83 | }; 84 | 85 | struct empty_struct 86 | { 87 | }; 88 | 89 | # ifndef FUNC_NO_EXCEPTIONS 90 | template 91 | Result empty_call(const functor_padding &, Arguments...) 92 | { 93 | throw bad_function_call(); 94 | } 95 | # endif 96 | 97 | template 98 | struct is_inplace_allocated 99 | { 100 | static const bool value 101 | // so that it fits 102 | = sizeof(T) <= sizeof(functor_padding) 103 | // so that it will be aligned 104 | && std::alignment_of::value % std::alignment_of::value == 0 105 | // so that we can offer noexcept move 106 | && std::is_nothrow_move_constructible::value 107 | // so that the user can override it 108 | && !force_function_heap_allocation::value; 109 | }; 110 | 111 | template 112 | T to_functor(T && func) 113 | { 114 | return FUNC_FORWARD(T, func); 115 | } 116 | template 117 | auto to_functor(Result (Class::*func)(Arguments...)) -> decltype(std::mem_fn(func)) 118 | { 119 | return std::mem_fn(func); 120 | } 121 | template 122 | auto to_functor(Result (Class::*func)(Arguments...) const) -> decltype(std::mem_fn(func)) 123 | { 124 | return std::mem_fn(func); 125 | } 126 | 127 | template 128 | struct functor_type 129 | { 130 | typedef decltype(to_functor(std::declval())) type; 131 | }; 132 | 133 | template 134 | bool is_null(const T &) 135 | { 136 | return false; 137 | } 138 | template 139 | bool is_null(Result (* const & function_pointer)(Arguments...)) 140 | { 141 | return function_pointer == nullptr; 142 | } 143 | template 144 | bool is_null(Result (Class::* const & function_pointer)(Arguments...)) 145 | { 146 | return function_pointer == nullptr; 147 | } 148 | template 149 | bool is_null(Result (Class::* const & function_pointer)(Arguments...) const) 150 | { 151 | return function_pointer == nullptr; 152 | } 153 | 154 | template 155 | struct is_valid_function_argument 156 | { 157 | static const bool value = false; 158 | }; 159 | 160 | template 161 | struct is_valid_function_argument, Result (Arguments...)> 162 | { 163 | static const bool value = false; 164 | }; 165 | 166 | template 167 | struct is_valid_function_argument 168 | { 169 | # ifdef _MSC_VER 170 | // as of january 2013 visual studio doesn't support the SFINAE below 171 | static const bool value = true; 172 | # else 173 | template 174 | static decltype(to_functor(std::declval())(std::declval()...)) check(U *); 175 | template 176 | static empty_struct check(...); 177 | 178 | static const bool value = std::is_convertible(nullptr)), Result>::value; 179 | # endif 180 | }; 181 | 182 | typedef function_manager * manager_type; 183 | 184 | struct manager_storage_type 185 | { 186 | template 187 | Allocator & get_allocator() FUNC_NOEXCEPT 188 | { 189 | return reinterpret_cast(manager); 190 | } 191 | template 192 | const Allocator & get_allocator() const FUNC_NOEXCEPT 193 | { 194 | return reinterpret_cast(manager); 195 | } 196 | 197 | functor_padding functor; 198 | manager_type manager; 199 | }; 200 | 201 | template 202 | struct function_manager_inplace_specialization 203 | { 204 | template 205 | static Result call(const functor_padding & storage, Arguments... arguments) 206 | { 207 | // do not call get_functor_ref because I want this function to be fast 208 | // in debug when nothing gets inlined 209 | return reinterpret_cast(const_cast(storage))(FUNC_FORWARD(Arguments, arguments)...); 210 | } 211 | 212 | static void store_functor(manager_storage_type & storage, T to_store) 213 | { 214 | new (&get_functor_ref(storage)) T(FUNC_FORWARD(T, to_store)); 215 | } 216 | static void move_functor(manager_storage_type & lhs, manager_storage_type && rhs) FUNC_NOEXCEPT 217 | { 218 | new (&get_functor_ref(lhs)) T(FUNC_MOVE(get_functor_ref(rhs))); 219 | } 220 | static void destroy_functor(Allocator &, manager_storage_type & storage) FUNC_NOEXCEPT 221 | { 222 | get_functor_ref(storage).~T(); 223 | } 224 | static T & get_functor_ref(const manager_storage_type & storage) FUNC_NOEXCEPT 225 | { 226 | return reinterpret_cast(const_cast(storage.functor)); 227 | } 228 | }; 229 | template 230 | struct function_manager_inplace_specialization::value>::type> 231 | { 232 | template 233 | static Result call(const functor_padding & storage, Arguments... arguments) 234 | { 235 | // do not call get_functor_ptr_ref because I want this function to be fast 236 | // in debug when nothing gets inlined 237 | return (*reinterpret_cast::pointer &>(const_cast(storage)))(FUNC_FORWARD(Arguments, arguments)...); 238 | } 239 | 240 | static void store_functor(manager_storage_type & self, T to_store) 241 | { 242 | Allocator & allocator = self.get_allocator();; 243 | static_assert(sizeof(typename std::allocator_traits::pointer) <= sizeof(self.functor), "The allocator's pointer type is too big"); 244 | typename std::allocator_traits::pointer * ptr = new (&get_functor_ptr_ref(self)) typename std::allocator_traits::pointer(std::allocator_traits::allocate(allocator, 1)); 245 | std::allocator_traits::construct(allocator, *ptr, FUNC_FORWARD(T, to_store)); 246 | } 247 | static void move_functor(manager_storage_type & lhs, manager_storage_type && rhs) FUNC_NOEXCEPT 248 | { 249 | static_assert(std::is_nothrow_move_constructible::pointer>::value, "we can't offer a noexcept swap if the pointer type is not nothrow move constructible"); 250 | new (&get_functor_ptr_ref(lhs)) typename std::allocator_traits::pointer(FUNC_MOVE(get_functor_ptr_ref(rhs))); 251 | // this next assignment makes the destroy function easier 252 | get_functor_ptr_ref(rhs) = nullptr; 253 | } 254 | static void destroy_functor(Allocator & allocator, manager_storage_type & storage) FUNC_NOEXCEPT 255 | { 256 | typename std::allocator_traits::pointer & pointer = get_functor_ptr_ref(storage); 257 | if (!pointer) return; 258 | std::allocator_traits::destroy(allocator, pointer); 259 | std::allocator_traits::deallocate(allocator, pointer, 1); 260 | } 261 | static T & get_functor_ref(const manager_storage_type & storage) FUNC_NOEXCEPT 262 | { 263 | return *get_functor_ptr_ref(storage); 264 | } 265 | static typename std::allocator_traits::pointer & get_functor_ptr_ref(const manager_storage_type & storage) FUNC_NOEXCEPT 266 | { 267 | return reinterpret_cast::pointer &>(const_cast(storage.functor)); 268 | } 269 | }; 270 | 271 | template 272 | static function_manager & get_default_manager(); 273 | 274 | template 275 | static void create_manager(manager_storage_type & storage, Allocator && allocator) 276 | { 277 | new (&storage.get_allocator()) Allocator(FUNC_MOVE(allocator)); 278 | storage.manager = &get_default_manager(); 279 | } 280 | 281 | // this struct acts as a vtable. it is an optimization to prevent 282 | // code-bloat from rtti. see the documentation of boost::function 283 | struct function_manager 284 | { 285 | template 286 | inline static function_manager create_default_manager() 287 | { 288 | function_manager result = 289 | { 290 | &templated_call_move_and_destroy, 291 | &templated_call_destroy, 292 | # ifndef FUNC_NO_RTTI 293 | typeid(T), 294 | &templated_call_target 295 | # endif 296 | }; 297 | return result; 298 | } 299 | 300 | void (*call_move_and_destroy)(manager_storage_type & lhs, manager_storage_type && rhs); 301 | void (*call_destroy)(manager_storage_type & manager); 302 | # ifndef FUNC_NO_RTTI 303 | const std::type_info & type_id; 304 | void * (*call_target)(manager_storage_type & manager, const std::type_info & type); 305 | # endif 306 | 307 | template 308 | static void templated_call_move_and_destroy(manager_storage_type & lhs, manager_storage_type && rhs) 309 | { 310 | typedef function_manager_inplace_specialization specialization; 311 | specialization::move_functor(lhs, FUNC_MOVE(rhs)); 312 | specialization::destroy_functor(rhs.get_allocator(), rhs); 313 | create_manager(lhs, FUNC_MOVE(rhs.get_allocator())); 314 | rhs.get_allocator().~Allocator(); 315 | } 316 | template 317 | static void templated_call_destroy(manager_storage_type & self) 318 | { 319 | typedef function_manager_inplace_specialization specialization; 320 | specialization::destroy_functor(self.get_allocator(), self); 321 | self.get_allocator().~Allocator(); 322 | } 323 | # ifndef FUNC_NO_RTTI 324 | template 325 | static void * templated_call_target(manager_storage_type & self, const std::type_info & type) 326 | { 327 | typedef function_manager_inplace_specialization specialization; 328 | if (type == typeid(T)) 329 | return &specialization::get_functor_ref(self); 330 | else 331 | return nullptr; 332 | } 333 | # endif 334 | }; 335 | template 336 | inline static function_manager & get_default_manager() 337 | { 338 | static function_manager default_manager = function_manager::create_default_manager(); 339 | return default_manager; 340 | } 341 | 342 | template 343 | struct typedeffer 344 | { 345 | typedef Result result_type; 346 | }; 347 | template 348 | struct typedeffer 349 | { 350 | typedef Result result_type; 351 | typedef Argument argument_type; 352 | }; 353 | template 354 | struct typedeffer 355 | { 356 | typedef Result result_type; 357 | typedef First_Argument first_argument_type; 358 | typedef Second_Argument second_argument_type; 359 | }; 360 | } 361 | 362 | template 363 | class movable_function 364 | : public detail::typedeffer 365 | { 366 | public: 367 | movable_function() FUNC_NOEXCEPT 368 | { 369 | initialize_empty(); 370 | } 371 | movable_function(std::nullptr_t) FUNC_NOEXCEPT 372 | { 373 | initialize_empty(); 374 | } 375 | movable_function(movable_function && other) FUNC_NOEXCEPT 376 | { 377 | initialize_empty(); 378 | swap(other); 379 | } 380 | movable_function(const movable_function & other) = delete; 381 | template 382 | movable_function(T functor, 383 | typename std::enable_if::value, detail::empty_struct>::type = detail::empty_struct()) FUNC_TEMPLATE_NOEXCEPT(T, std::allocator::type>) 384 | { 385 | if (detail::is_null(functor)) 386 | { 387 | initialize_empty(); 388 | } 389 | else 390 | { 391 | typedef typename detail::functor_type::type functor_type; 392 | initialize(detail::to_functor(FUNC_FORWARD(T, functor)), std::allocator()); 393 | } 394 | } 395 | template 396 | movable_function(std::allocator_arg_t, const Allocator &) 397 | { 398 | // ignore the allocator because I don't allocate 399 | initialize_empty(); 400 | } 401 | template 402 | movable_function(std::allocator_arg_t, const Allocator &, std::nullptr_t) 403 | { 404 | // ignore the allocator because I don't allocate 405 | initialize_empty(); 406 | } 407 | template 408 | movable_function(std::allocator_arg_t, const Allocator & allocator, T functor, 409 | typename std::enable_if::value, detail::empty_struct>::type = detail::empty_struct()) 410 | FUNC_TEMPLATE_NOEXCEPT(T, Allocator) 411 | { 412 | if (detail::is_null(functor)) 413 | { 414 | initialize_empty(); 415 | } 416 | else 417 | { 418 | initialize(detail::to_functor(FUNC_FORWARD(T, functor)), Allocator(allocator)); 419 | } 420 | } 421 | template 422 | movable_function(std::allocator_arg_t, const Allocator & allocator, const movable_function & other) = delete; 423 | template 424 | movable_function(std::allocator_arg_t, const Allocator &, movable_function && other) FUNC_NOEXCEPT 425 | { 426 | // ignore the allocator because I don't allocate 427 | initialize_empty(); 428 | swap(other); 429 | } 430 | 431 | movable_function & operator=(movable_function other) FUNC_NOEXCEPT 432 | { 433 | swap(other); 434 | return *this; 435 | } 436 | ~movable_function() FUNC_NOEXCEPT 437 | { 438 | manager_storage.manager->call_destroy(manager_storage); 439 | } 440 | 441 | Result operator()(Arguments... arguments) const 442 | { 443 | return call(manager_storage.functor, FUNC_FORWARD(Arguments, arguments)...); 444 | } 445 | 446 | template 447 | void assign(T && functor, const Allocator & allocator) FUNC_TEMPLATE_NOEXCEPT(T, Allocator) 448 | { 449 | function(std::allocator_arg, allocator, functor).swap(*this); 450 | } 451 | 452 | void swap(movable_function & other) FUNC_NOEXCEPT 453 | { 454 | detail::manager_storage_type temp_storage; 455 | other.manager_storage.manager->call_move_and_destroy(temp_storage, FUNC_MOVE(other.manager_storage)); 456 | manager_storage.manager->call_move_and_destroy(other.manager_storage, FUNC_MOVE(manager_storage)); 457 | temp_storage.manager->call_move_and_destroy(manager_storage, FUNC_MOVE(temp_storage)); 458 | 459 | std::swap(call, other.call); 460 | } 461 | 462 | 463 | # ifndef FUNC_NO_RTTI 464 | const std::type_info & target_type() const FUNC_NOEXCEPT 465 | { 466 | return manager_storage.manager->type_id; 467 | } 468 | template 469 | T * target() FUNC_NOEXCEPT 470 | { 471 | return static_cast(manager_storage.manager->call_target(manager_storage, typeid(T))); 472 | } 473 | template 474 | const T * target() const FUNC_NOEXCEPT 475 | { 476 | return static_cast(manager_storage.manager->call_target(const_cast(manager_storage), typeid(T))); 477 | } 478 | # endif 479 | 480 | operator bool() const FUNC_NOEXCEPT 481 | { 482 | 483 | # ifdef FUNC_NO_EXCEPTIONS 484 | return call != nullptr; 485 | # else 486 | return call != &detail::empty_call; 487 | # endif 488 | } 489 | 490 | private: 491 | detail::manager_storage_type manager_storage; 492 | Result (*call)(const detail::functor_padding &, Arguments...); 493 | 494 | template 495 | void initialize(T functor, Allocator && allocator) 496 | { 497 | call = &detail::function_manager_inplace_specialization::template call; 498 | detail::create_manager(manager_storage, FUNC_FORWARD(Allocator, allocator)); 499 | detail::function_manager_inplace_specialization::store_functor(manager_storage, FUNC_FORWARD(T, functor)); 500 | } 501 | 502 | typedef Result(*Empty_Function_Type)(Arguments...); 503 | void initialize_empty() FUNC_NOEXCEPT 504 | { 505 | typedef std::allocator Allocator; 506 | static_assert(detail::is_inplace_allocated::value, "The empty function should benefit from small functor optimization"); 507 | 508 | detail::create_manager(manager_storage, Allocator()); 509 | detail::function_manager_inplace_specialization::store_functor(manager_storage, nullptr); 510 | # ifdef FUNC_NO_EXCEPTIONS 511 | call = nullptr; 512 | # else 513 | call = &detail::empty_call; 514 | # endif 515 | } 516 | }; 517 | 518 | template 519 | bool operator==(std::nullptr_t, const movable_function & rhs) FUNC_NOEXCEPT 520 | { 521 | return !rhs; 522 | } 523 | template 524 | bool operator==(const movable_function & lhs, std::nullptr_t) FUNC_NOEXCEPT 525 | { 526 | return !lhs; 527 | } 528 | template 529 | bool operator!=(std::nullptr_t, const movable_function & rhs) FUNC_NOEXCEPT 530 | { 531 | return rhs; 532 | } 533 | template 534 | bool operator!=(const movable_function & lhs, std::nullptr_t) FUNC_NOEXCEPT 535 | { 536 | return lhs; 537 | } 538 | 539 | template 540 | void swap(movable_function & lhs, movable_function & rhs) 541 | { 542 | lhs.swap(rhs); 543 | } 544 | 545 | } // end namespace func 546 | 547 | namespace std 548 | { 549 | template 550 | struct uses_allocator, Allocator> 551 | : std::true_type 552 | { 553 | }; 554 | } 555 | 556 | #ifdef __GNUC__ 557 | #pragma GCC diagnostic pop 558 | #endif 559 | #undef FUNC_NOEXCEPT 560 | #undef FUNC_TEMPLATE_NOEXCEPT 561 | #undef FUNC_FORWARD 562 | #undef FUNC_MOVE 563 | -------------------------------------------------------------------------------- /await/includeOnly.cpp: -------------------------------------------------------------------------------- 1 | //#include "boost_await.h" 2 | #include "await.h" 3 | -------------------------------------------------------------------------------- /await/stack_swap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #include "stack_swap.h" 28 | #include 29 | #include 30 | 31 | namespace stack 32 | { 33 | extern "C" void switch_to_context(void ** old_stack_top, const void * new_stack_top); 34 | extern "C" void callable_context_start(); 35 | #ifndef _WIN64 36 | # ifdef CORO_LINUX_ABI_DEBUGGING_HELP 37 | extern "C" void switch_to_callable_context(void ** old_stack_top, const void * new_stack_top, void * rbp_on_stack); 38 | extern "C" void context_switch_point(); 39 | # endif 40 | asm 41 | ( 42 | "switch_to_context:\n\t" 43 | "pushq %rbp\n\t" 44 | "movq %rsp, %rbp\n\t" 45 | // store rbx and r12 to r15 on the stack. these will be restored 46 | // after we switch back 47 | "pushq %rbx\n\t" 48 | "pushq %r12\n\t" 49 | "pushq %r13\n\t" 50 | "pushq %r14\n\t" 51 | "pushq %r15\n\t" 52 | "movq %rsp, (%rdi)\n\t" // store stack pointer 53 | // set up the other guy's stack pointer 54 | "context_switch_point:\n\t" 55 | "movq %rsi, %rsp\n\t" 56 | // and we are now in the other context 57 | // restore registers 58 | "popq %r15\n\t" 59 | "popq %r14\n\t" 60 | "popq %r13\n\t" 61 | "popq %r12\n\t" 62 | "popq %rbx\n\t" 63 | "popq %rbp\n\t" 64 | "retq\n\t" // go to whichever code is used by the other stack 65 | ); 66 | # ifdef CORO_LINUX_ABI_DEBUGGING_HELP 67 | asm 68 | ( 69 | "switch_to_callable_context:\n\t" 70 | "pushq %rbp\n\t" 71 | "movq %rsp, %rbp\n\t" 72 | // store rbx and r12 to r15 on the stack. these will be restored 73 | // after we jump back 74 | "pushq %rbx\n\t" 75 | "pushq %r12\n\t" 76 | "pushq %r13\n\t" 77 | "pushq %r14\n\t" 78 | "pushq %r15\n\t" 79 | "movq %rsp, (%rdi)\n\t" // store stack pointer 80 | // set up the other guy's stack pointer to make 81 | // debugging easier 82 | "movq %rbp, (%rdx)\n\t" 83 | "jmp context_switch_point\n\t" 84 | ); 85 | # endif 86 | 87 | asm 88 | ( 89 | "callable_context_start:\n\t" 90 | # ifdef CORO_LINUX_ABI_DEBUGGING_HELP 91 | "popq %rbp\n\t" // these pop rbp push rbp instructions don't do anything but they make the 92 | "pushq (%rsp)\n\t" // debugger less confused. without these the call stack breaks inside coroutines 93 | // unfortunately even with these I can't get callstacks to work for more than one 94 | // layer of coroutines. 95 | # endif 96 | "movq %r13, %rdi\n\t" // function_argument 97 | "callq *%r12\n\t" // function 98 | "movq (%rbx), %rsi\n\t" // caller_stack_top 99 | "jmp context_switch_point\n\t" 100 | ); 101 | #endif 102 | 103 | void stack_context::switch_into() 104 | { 105 | #ifdef CORO_LINUX_ABI_DEBUGGING_HELP 106 | switch_to_callable_context(&caller_stack_top, my_stack_top, rbp_on_stack); 107 | #else 108 | switch_to_context(&caller_stack_top, my_stack_top); 109 | #endif 110 | } 111 | void stack_context::switch_out_of() 112 | { 113 | switch_to_context(&my_stack_top, caller_stack_top); 114 | } 115 | 116 | static void * ensure_alignment(void * stack, size_t stack_size) 117 | { 118 | static const size_t CONTEXT_STACK_ALIGNMENT = 16; 119 | unsigned char * stack_top = static_cast(stack) + stack_size; 120 | // if the user gave me a non-aligned stack, just cut a couple bytes off from the top 121 | return stack_top - reinterpret_cast(stack_top) % CONTEXT_STACK_ALIGNMENT; 122 | } 123 | 124 | void stack_context::reset(void * stack, size_t stack_size, void (*function)(void *), void * function_argument) 125 | { 126 | unsigned char * math_stack = static_cast(ensure_alignment(stack, stack_size)); 127 | #ifdef _WIN64 128 | my_stack_top = math_stack - sizeof(void *) // space for return address (initial call) 129 | - sizeof(void *) * 2 // space for stack info 130 | - sizeof(void *) * 4 // space for arguments 131 | - sizeof(void *) * 8 // space for non-volatile integer registers 132 | //- sizeof(void *) * 2 * 10 // space for non-volatile xmm registers 133 | //- sizeof(void *) // stack alignment 134 | ; 135 | void ** initial_stack = static_cast(my_stack_top); 136 | // initial_stack[11] to initial_stack[14] are space for arguments. I won't 137 | // use that space but the calling convention says it has to be there 138 | initial_stack[10] = &callable_context_start; 139 | initial_stack[9] = math_stack; 140 | initial_stack[8] = stack; 141 | initial_stack[7] = &caller_stack_top; // initial rbx 142 | initial_stack[6] = function; // initial rbp 143 | initial_stack[5] = function_argument; // initial rdi 144 | initial_stack[4] = nullptr; // initial rsi 145 | initial_stack[3] = nullptr; // initial r12 146 | initial_stack[2] = nullptr; // initial r13 147 | initial_stack[1] = nullptr; // initial r14 148 | initial_stack[0] = nullptr; // initial r15 149 | initial_stack[-1] = nullptr; // stack alignment 150 | initial_stack[-3] = initial_stack[-2] = nullptr; // initial xmm6 151 | initial_stack[-5] = initial_stack[-4] = nullptr; // initial xmm7 152 | initial_stack[-7] = initial_stack[-6] = nullptr; // initial xmm8 153 | initial_stack[-9] = initial_stack[-8] = nullptr; // initial xmm9 154 | initial_stack[-11] = initial_stack[-10] = nullptr; // initial xmm10 155 | initial_stack[-13] = initial_stack[-12] = nullptr; // initial xmm11 156 | initial_stack[-15] = initial_stack[-14] = nullptr; // initial xmm12 157 | initial_stack[-17] = initial_stack[-16] = nullptr; // initial xmm13 158 | initial_stack[-19] = initial_stack[-18] = nullptr; // initial xmm14 159 | initial_stack[-21] = initial_stack[-20] = nullptr; // initial xmm15 160 | #else 161 | # ifdef CORO_LINUX_ABI_DEBUGGING_HELP 162 | my_stack_top = math_stack - sizeof(void *) * 9; 163 | void ** initial_stack = static_cast(my_stack_top); 164 | // store the return address here to make the debuggers life easier 165 | initial_stack[8] = reinterpret_cast(&context_switch_point); 166 | initial_stack[7] = nullptr; // will store rbp here to make the debuggers life easier 167 | rbp_on_stack = &initial_stack[7]; 168 | # else 169 | my_stack_top = math_stack - sizeof(void *) * 7; 170 | void ** initial_stack = static_cast(my_stack_top); 171 | # endif 172 | initial_stack[6] = reinterpret_cast(&callable_context_start); 173 | initial_stack[5] = &initial_stack[7]; // initial rbp 174 | initial_stack[4] = &caller_stack_top; // initial rbx 175 | initial_stack[3] = reinterpret_cast(function); // initial r12 176 | initial_stack[2] = function_argument; // initial r13 177 | initial_stack[1] = nullptr; // initial r14 178 | initial_stack[0] = nullptr; // initial r15 179 | #endif 180 | } 181 | 182 | 183 | stack_context::stack_context(void * stack, size_t stack_size, void (*function)(void *), void * function_argument) 184 | : caller_stack_top(nullptr), my_stack_top(nullptr) 185 | #ifdef CORO_LINUX_ABI_DEBUGGING_HELP 186 | , rbp_on_stack(nullptr) 187 | #endif 188 | { 189 | reset(stack, stack_size, function, function_argument); 190 | } 191 | 192 | } 193 | 194 | 195 | #ifndef DISABLE_GTEST 196 | #include 197 | 198 | namespace 199 | { 200 | struct exception_test_info 201 | { 202 | stack::stack_context * context; 203 | int * to_set; 204 | }; 205 | void exception_call(void * arg) 206 | { 207 | exception_test_info * info = static_cast(arg); 208 | try 209 | { 210 | info->context->switch_out_of(); 211 | } 212 | catch(int i) 213 | { 214 | *info->to_set = i; 215 | } 216 | } 217 | } 218 | 219 | TEST(stack_swap, exceptions) 220 | { 221 | unsigned char local_stack[64*1024]; 222 | exception_test_info info; 223 | 224 | stack::stack_context context(local_stack, sizeof(local_stack), &exception_call, &info); 225 | info.context = &context; 226 | int inner_set = 0; 227 | info.to_set = &inner_set; 228 | int outer_set = 0; 229 | try 230 | { 231 | context.switch_into(); 232 | throw 5; 233 | } 234 | catch(int i) 235 | { 236 | outer_set = i; 237 | } 238 | EXPECT_EQ(0, inner_set); 239 | EXPECT_EQ(5, outer_set); 240 | } 241 | #endif 242 | 243 | -------------------------------------------------------------------------------- /await/stack_swap.h: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #pragma once 28 | 29 | #include 30 | 31 | namespace stack 32 | { 33 | struct stack_context 34 | { 35 | stack_context(void * stack, size_t stack_size, void (*function)(void *), void * function_argument); 36 | void switch_into(); 37 | void switch_out_of(); 38 | 39 | void reset(void * stack, size_t stack_size, void (*function)(void *), void * function_argument); 40 | 41 | private: 42 | void * caller_stack_top; 43 | void * my_stack_top; 44 | #ifdef CORO_LINUX_ABI_DEBUGGING_HELP 45 | void ** rbp_on_stack; 46 | #endif 47 | 48 | // intentionally left unimplemented. there is a this pointer stored on the 49 | // stack and as such not even moving makes sense, because then that this 50 | // pointer would point to the old address 51 | stack_context(const stack_context &); 52 | stack_context & operator=(const stack_context &); 53 | stack_context(stack_context &&); 54 | stack_context & operator=(stack_context &&); 55 | }; 56 | } // end namespace stack 57 | -------------------------------------------------------------------------------- /await/stack_swap_asm.asm: -------------------------------------------------------------------------------- 1 | .code 2 | ; This is free and unencumbered software released into the public domain. 3 | ; 4 | ; Anyone is free to copy, modify, publish, use, compile, sell, or 5 | ; distribute this software, either in source code form or as a compiled 6 | ; binary, for any purpose, commercial or non-commercial, and by any 7 | ; means. 8 | ; 9 | ; In jurisdictions that recognize copyright laws, the author or authors 10 | ; of this software dedicate any and all copyright interest in the 11 | ; software to the public domain. We make this dedication for the benefit 12 | ; of the public at large and to the detriment of our heirs and 13 | ; successors. We intend this dedication to be an overt act of 14 | ; relinquishment in perpetuity of all present and future rights to this 15 | ; software under copyright law. 16 | ; 17 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | ; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | ; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | ; IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | ; OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | ; OTHER DEALINGS IN THE SOFTWARE. 24 | ; 25 | ; For more information, please refer to 26 | 27 | switch_to_context proc 28 | ; store NT_TIB stack info members 29 | push qword ptr gs:[8] 30 | push qword ptr gs:[16] 31 | ; store rbx and r12 to r15 on the stack. these will be restored 32 | ; after we switch back 33 | push rbx 34 | push rbp 35 | push rdi 36 | push rsi 37 | push r12 38 | push r13 39 | push r14 40 | push r15 41 | ; start at -24 instead of -16 because of alignment 42 | movaps [rsp - 24], xmm6 43 | movaps [rsp - 40], xmm7 44 | movaps [rsp - 56], xmm8 45 | movaps [rsp - 72], xmm9 46 | movaps [rsp - 88], xmm10 47 | movaps [rsp - 104], xmm11 48 | movaps [rsp - 120], xmm12 49 | movaps [rsp - 136], xmm13 50 | movaps [rsp - 152], xmm14 51 | movaps [rsp - 168], xmm15 52 | mov qword ptr [rcx], rsp ; store stack pointer 53 | ; fall through 54 | switch_to_context endp 55 | 56 | context_switch_point proc 57 | ; set up the other guy's stack pointers 58 | mov rsp, rdx 59 | ; and we are now in the other context 60 | ; restore registers 61 | ; start at -168 instead of -160 because of alignment 62 | movaps xmm15, [rsp - 168] 63 | movaps xmm14, [rsp - 152] 64 | movaps xmm13, [rsp - 136] 65 | movaps xmm12, [rsp - 120] 66 | movaps xmm11, [rsp - 104] 67 | movaps xmm10, [rsp - 88] 68 | movaps xmm9, [rsp - 72] 69 | movaps xmm8, [rsp - 56] 70 | movaps xmm7, [rsp - 40] 71 | movaps xmm6, [rsp - 24] 72 | pop r15 73 | pop r14 74 | pop r13 75 | pop r12 76 | pop rsi 77 | pop rdi 78 | pop rbp 79 | pop rbx 80 | ; restore NT_TIB stack info members. this is needed because _chkstk will 81 | ; use these members to step the stack. so we need to make sure that it, and 82 | ; any other functions that use the stack information, get correct values 83 | pop qword ptr gs:[16] 84 | pop qword ptr gs:[8] 85 | ret ; go to whichever code is used by the other stack 86 | context_switch_point endp 87 | 88 | callable_context_start proc 89 | mov rcx, rdi ; function_argument 90 | call rbp ; function 91 | mov rdx, [rbx] ; caller_stack_top 92 | jmp context_switch_point 93 | callable_context_start endp 94 | 95 | end 96 | -------------------------------------------------------------------------------- /await/then_future.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #include "then_future.h" 28 | 29 | #ifndef DISABLE_GTEST 30 | #include 31 | static std::atomic num_allocations(0); 32 | static std::atomic num_frees(0); 33 | //#define TEST_MEMORY_LEAKS 34 | #ifdef TEST_MEMORY_LEAKS 35 | void * operator new(size_t size) 36 | { 37 | ++num_allocations; 38 | return malloc(size); 39 | } 40 | void * operator new[](size_t size) 41 | { 42 | ++num_allocations; 43 | return malloc(size); 44 | } 45 | void operator delete(void * ptr) noexcept 46 | { 47 | if (ptr) 48 | { 49 | ++num_frees; 50 | free(ptr); 51 | } 52 | } 53 | void operator delete[](void * ptr) noexcept 54 | { 55 | if (ptr) 56 | { 57 | ++num_frees; 58 | free(ptr); 59 | } 60 | } 61 | #endif 62 | namespace 63 | { 64 | struct ScopedAssertNoLeaks 65 | { 66 | ScopedAssertNoLeaks() 67 | : num_allocations_before(num_allocations), num_frees_before(num_frees) 68 | { 69 | } 70 | ~ScopedAssertNoLeaks() 71 | { 72 | [&]{ ASSERT_EQ(num_allocations - num_allocations_before, num_frees - num_frees_before); }(); 73 | } 74 | 75 | private: 76 | size_t num_allocations_before; 77 | size_t num_frees_before; 78 | }; 79 | 80 | TEST(then_future, simple) 81 | { 82 | ScopedAssertNoLeaks assert_no_leaks; 83 | then_promise promise; 84 | then_future future = promise.get_future(); 85 | promise.set_value(5); 86 | ASSERT_EQ(5, future.get()); 87 | } 88 | TEST(then_future, ref) 89 | { 90 | ScopedAssertNoLeaks assert_no_leaks; 91 | std::unique_ptr result(new int(5)); 92 | then_promise promise; 93 | then_future future = promise.get_future(); 94 | promise.set_value(*result); 95 | ASSERT_EQ(result.get(), &future.get()); 96 | } 97 | TEST(then_future, void) 98 | { 99 | ScopedAssertNoLeaks assert_no_leaks; 100 | then_promise promise; 101 | then_future future = promise.get_future(); 102 | promise.set_value(); 103 | future.get(); 104 | } 105 | TEST(then_future, then) 106 | { 107 | ScopedAssertNoLeaks assert_no_leaks; 108 | then_promise promise; 109 | then_future future = promise.get_future().then([](then_future & a){ return a.get() + 5; }); 110 | promise.set_value(5); 111 | ASSERT_EQ(10, future.get()); 112 | } 113 | TEST(then_future, wait) 114 | { 115 | ScopedAssertNoLeaks assert_no_leaks; 116 | then_promise promise; 117 | then_future future = promise.get_future(); 118 | std::thread run_delayed([&] 119 | { 120 | promise.set_value(5); 121 | }); 122 | ASSERT_EQ(5, future.get()); 123 | run_delayed.join(); 124 | } 125 | TEST(then_future, async_void) 126 | { 127 | ScopedAssertNoLeaks assert_no_leaks; 128 | bool ran = false; 129 | bool ran2 = false; 130 | custom_async([&] 131 | { 132 | ran = true; 133 | }).then([&](then_future &) 134 | { 135 | ASSERT_TRUE(ran); 136 | ran2 = true; 137 | }).get(); 138 | ASSERT_TRUE(ran2); 139 | } 140 | TEST(then_future, discard_future) 141 | { 142 | ScopedAssertNoLeaks assert_no_leaks; 143 | then_promise promise; 144 | bool ran = false; 145 | promise.get_future().then([&ran](then_future &){ ran = true; }); 146 | promise.set_value(); 147 | ASSERT_TRUE(ran); 148 | } 149 | TEST(then_future, discard_promise) 150 | { 151 | ScopedAssertNoLeaks assert_no_leaks; 152 | then_future future = then_promise().get_future(); 153 | bool thrown = false; 154 | try 155 | { 156 | future.get(); 157 | } 158 | catch(const std::future_error & error) 159 | { 160 | thrown = true; 161 | ASSERT_EQ(std::future_errc::broken_promise, error.code()); 162 | } 163 | ASSERT_TRUE(thrown); 164 | } 165 | TEST(then_future, discard_both) 166 | { 167 | ScopedAssertNoLeaks assert_no_leaks; 168 | then_promise().get_future().then([](then_future &){}); 169 | } 170 | TEST(then_future, discard_async) 171 | { 172 | ScopedAssertNoLeaks assert_no_leaks; 173 | bool ran1 = false; 174 | custom_async([&ran1] 175 | { 176 | ran1 = true; 177 | }); 178 | ASSERT_TRUE(ran1); 179 | bool ran2 = false; 180 | custom_async([]{}).then([&ran2](then_future &) 181 | { 182 | ran2 = true; 183 | }); 184 | ASSERT_TRUE(ran2); 185 | } 186 | TEST(then_future, set_before_get_future) 187 | { 188 | ScopedAssertNoLeaks assert_no_leaks; 189 | then_promise promise; 190 | promise.set_value(); 191 | bool ran = false; 192 | promise.get_future().then([&ran](then_future &){ ran = true; }); 193 | ASSERT_TRUE(ran); 194 | } 195 | 196 | struct MovableFunctor 197 | { 198 | MovableFunctor() 199 | : a(new int(5)) 200 | { 201 | } 202 | 203 | int operator()(then_future &) 204 | { 205 | return *a; 206 | } 207 | 208 | std::unique_ptr a; 209 | }; 210 | // this test is here mainly to ensure that the code 211 | // compiles with functors that are movable only 212 | TEST(then_future, movable) 213 | { 214 | ScopedAssertNoLeaks assert_no_leaks; 215 | then_promise promise; 216 | then_future future = promise.get_future().then(MovableFunctor()); 217 | promise.set_value(); 218 | ASSERT_EQ(5, future.get()); 219 | } 220 | TEST(then_future, valid) 221 | { 222 | ScopedAssertNoLeaks assert_no_leaks; 223 | then_promise promise; 224 | then_future future; 225 | ASSERT_FALSE(future.valid()); 226 | future = promise.get_future(); 227 | ASSERT_TRUE(future.valid()); 228 | then_future then = future.then([](then_future &){}); 229 | ASSERT_FALSE(future.valid()); 230 | ASSERT_TRUE(then.valid()); 231 | } 232 | } 233 | #endif 234 | -------------------------------------------------------------------------------- /await/then_future.h: -------------------------------------------------------------------------------- 1 | /* 2 | This is free and unencumbered software released into the public domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | */ 27 | #pragma once 28 | 29 | #include 30 | #include "function.hpp" 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | template 37 | struct then_promise; 38 | template 39 | struct then_future; 40 | 41 | namespace detail 42 | { 43 | struct two_thread_gate 44 | { 45 | two_thread_gate() 46 | : count(0) 47 | { 48 | } 49 | void reset() 50 | { 51 | count = 0; 52 | } 53 | // will return true when the second thread signals 54 | bool signal() 55 | { 56 | return ++count == 2; 57 | } 58 | 59 | private: 60 | std::atomic count; 61 | }; 62 | 63 | template 64 | struct continuation 65 | { 66 | void run(then_future & self) 67 | { 68 | if (signal.signal()) next(self); 69 | } 70 | 71 | template 72 | void set(then_future & self, Func && value) 73 | { 74 | next = std::forward(value); 75 | run(self); 76 | } 77 | 78 | private: 79 | func::movable_function &)> next; 80 | two_thread_gate signal; 81 | }; 82 | 83 | template 84 | struct future_shared_state 85 | { 86 | std::future future; 87 | continuation cont; 88 | }; 89 | 90 | template 91 | void set_promise_value(Promise & promise, Func && func) 92 | { 93 | try 94 | { 95 | promise.set_value(func()); 96 | } 97 | catch(...) // no need for special handling of the case that set_value() 98 | // throws because set_exception() will throw the same exception 99 | { 100 | promise.set_exception(std::current_exception()); 101 | } 102 | } 103 | template