├── 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