Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 10:03:45

0001 ///////////////////////////////////////////////////////////////////////////////
0002 // Copyright (c) Lewis Baker
0003 // Licenced under MIT license. See LICENSE.txt for details.
0004 ///////////////////////////////////////////////////////////////////////////////
0005 #ifndef CPPCORO_DETAIL_SYNC_WAIT_TASK_HPP_INCLUDED
0006 #define CPPCORO_DETAIL_SYNC_WAIT_TASK_HPP_INCLUDED
0007 
0008 #include <cppcoro/config.hpp>
0009 #include <cppcoro/awaitable_traits.hpp>
0010 #include <cppcoro/detail/lightweight_manual_reset_event.hpp>
0011 
0012 #include <cppcoro/coroutine.hpp>
0013 #include <cassert>
0014 #include <exception>
0015 #include <utility>
0016 
0017 namespace cppcoro
0018 {
0019     namespace detail
0020     {
0021         template<typename RESULT>
0022         class sync_wait_task;
0023 
0024         template<typename RESULT>
0025         class sync_wait_task_promise final
0026         {
0027             using coroutine_handle_t = cppcoro::coroutine_handle<sync_wait_task_promise<RESULT>>;
0028 
0029         public:
0030 
0031             using reference = RESULT&&;
0032 
0033             sync_wait_task_promise() noexcept
0034             {}
0035 
0036             void start(detail::lightweight_manual_reset_event& event)
0037             {
0038                 m_event = &event;
0039                 coroutine_handle_t::from_promise(*this).resume();
0040             }
0041 
0042             auto get_return_object() noexcept
0043             {
0044                 return coroutine_handle_t::from_promise(*this);
0045             }
0046 
0047             cppcoro::suspend_always initial_suspend() noexcept
0048             {
0049                 return{};
0050             }
0051 
0052             auto final_suspend() noexcept
0053             {
0054                 class completion_notifier
0055                 {
0056                 public:
0057 
0058                     bool await_ready() const noexcept { return false; }
0059 
0060                     void await_suspend(coroutine_handle_t coroutine) const noexcept
0061                     {
0062                         coroutine.promise().m_event->set();
0063                     }
0064 
0065                     void await_resume() noexcept {}
0066                 };
0067 
0068                 return completion_notifier{};
0069             }
0070 
0071 #if CPPCORO_COMPILER_MSVC && CPPCORO_COMPILER_MSVC < 19'20'00000
0072             // HACK: This is needed to work around a bug in MSVC 2017.7/2017.8.
0073             // See comment in make_sync_wait_task below.
0074             template<typename Awaitable>
0075             Awaitable&& await_transform(Awaitable&& awaitable)
0076             {
0077                 return static_cast<Awaitable&&>(awaitable);
0078             }
0079 
0080             struct get_promise_t {};
0081             static constexpr get_promise_t get_promise = {};
0082 
0083             auto await_transform(get_promise_t)
0084             {
0085                 class awaiter
0086                 {
0087                 public:
0088                     awaiter(sync_wait_task_promise* promise) noexcept : m_promise(promise) {}
0089                     bool await_ready() noexcept {
0090                         return true;
0091                     }
0092                     void await_suspend(cppcoro::coroutine_handle<>) noexcept {}
0093                     sync_wait_task_promise& await_resume() noexcept
0094                     {
0095                         return *m_promise;
0096                     }
0097                 private:
0098                     sync_wait_task_promise* m_promise;
0099                 };
0100                 return awaiter{ this };
0101             }
0102 #endif
0103 
0104             auto yield_value(reference result) noexcept
0105             {
0106                 m_result = std::addressof(result);
0107                 return final_suspend();
0108             }
0109 
0110             void return_void() noexcept
0111             {
0112                 // The coroutine should have either yielded a value or thrown
0113                 // an exception in which case it should have bypassed return_void().
0114                 assert(false);
0115             }
0116 
0117             void unhandled_exception()
0118             {
0119                 m_exception = std::current_exception();
0120             }
0121 
0122             reference result()
0123             {
0124                 if (m_exception)
0125                 {
0126                     std::rethrow_exception(m_exception);
0127                 }
0128 
0129                 return static_cast<reference>(*m_result);
0130             }
0131 
0132         private:
0133 
0134             detail::lightweight_manual_reset_event* m_event;
0135             std::remove_reference_t<RESULT>* m_result;
0136             std::exception_ptr m_exception;
0137 
0138         };
0139 
0140         template<>
0141         class sync_wait_task_promise<void>
0142         {
0143             using coroutine_handle_t = cppcoro::coroutine_handle<sync_wait_task_promise<void>>;
0144 
0145         public:
0146 
0147             sync_wait_task_promise() noexcept
0148             {}
0149 
0150             void start(detail::lightweight_manual_reset_event& event)
0151             {
0152                 m_event = &event;
0153                 coroutine_handle_t::from_promise(*this).resume();
0154             }
0155 
0156             auto get_return_object() noexcept
0157             {
0158                 return coroutine_handle_t::from_promise(*this);
0159             }
0160 
0161             cppcoro::suspend_always initial_suspend() noexcept
0162             {
0163                 return{};
0164             }
0165 
0166             auto final_suspend() noexcept
0167             {
0168                 class completion_notifier
0169                 {
0170                 public:
0171 
0172                     bool await_ready() const noexcept { return false; }
0173 
0174                     void await_suspend(coroutine_handle_t coroutine) const noexcept
0175                     {
0176                         coroutine.promise().m_event->set();
0177                     }
0178 
0179                     void await_resume() noexcept {}
0180                 };
0181 
0182                 return completion_notifier{};
0183             }
0184 
0185             void return_void() {}
0186 
0187             void unhandled_exception()
0188             {
0189                 m_exception = std::current_exception();
0190             }
0191 
0192             void result()
0193             {
0194                 if (m_exception)
0195                 {
0196                     std::rethrow_exception(m_exception);
0197                 }
0198             }
0199 
0200         private:
0201 
0202             detail::lightweight_manual_reset_event* m_event;
0203             std::exception_ptr m_exception;
0204 
0205         };
0206 
0207         template<typename RESULT>
0208         class sync_wait_task final
0209         {
0210         public:
0211 
0212             using promise_type = sync_wait_task_promise<RESULT>;
0213 
0214             using coroutine_handle_t = cppcoro::coroutine_handle<promise_type>;
0215 
0216             sync_wait_task(coroutine_handle_t coroutine) noexcept
0217                 : m_coroutine(coroutine)
0218             {}
0219 
0220             sync_wait_task(sync_wait_task&& other) noexcept
0221                 : m_coroutine(std::exchange(other.m_coroutine, coroutine_handle_t{}))
0222             {}
0223 
0224             ~sync_wait_task()
0225             {
0226                 if (m_coroutine) m_coroutine.destroy();
0227             }
0228 
0229             sync_wait_task(const sync_wait_task&) = delete;
0230             sync_wait_task& operator=(const sync_wait_task&) = delete;
0231 
0232             void start(lightweight_manual_reset_event& event) noexcept
0233             {
0234                 m_coroutine.promise().start(event);
0235             }
0236 
0237             decltype(auto) result()
0238             {
0239                 return m_coroutine.promise().result();
0240             }
0241 
0242         private:
0243 
0244             coroutine_handle_t m_coroutine;
0245 
0246         };
0247 
0248 #if CPPCORO_COMPILER_MSVC && CPPCORO_COMPILER_MSVC < 19'20'00000
0249         // HACK: Work around bug in MSVC where passing a parameter by universal reference
0250         // results in an error when passed a move-only type, complaining that the copy-constructor
0251         // has been deleted. The parameter should be passed by reference and the compiler should
0252         // notcalling the copy-constructor for the argument
0253         template<
0254             typename AWAITABLE,
0255             typename RESULT = typename cppcoro::awaitable_traits<AWAITABLE&&>::await_result_t,
0256             std::enable_if_t<!std::is_void_v<RESULT>, int> = 0>
0257         sync_wait_task<RESULT> make_sync_wait_task(AWAITABLE& awaitable)
0258         {
0259             // HACK: Workaround another bug in MSVC where the expression 'co_yield co_await x' seems
0260             // to completely ignore the co_yield an never calls promise.yield_value().
0261             // The coroutine seems to be resuming the 'co_await' after the 'co_yield'
0262             // rather than before the 'co_yield'.
0263             // This bug is present in VS 2017.7 and VS 2017.8.
0264             auto& promise = co_await sync_wait_task_promise<RESULT>::get_promise;
0265             co_await promise.yield_value(co_await std::forward<AWAITABLE>(awaitable));
0266 
0267             //co_yield co_await std::forward<AWAITABLE>(awaitable);
0268         }
0269 
0270         template<
0271             typename AWAITABLE,
0272             typename RESULT = typename cppcoro::awaitable_traits<AWAITABLE&&>::await_result_t,
0273             std::enable_if_t<std::is_void_v<RESULT>, int> = 0>
0274         sync_wait_task<void> make_sync_wait_task(AWAITABLE& awaitable)
0275         {
0276             co_await static_cast<AWAITABLE&&>(awaitable);
0277         }
0278 #else
0279         template<
0280             typename AWAITABLE,
0281             typename RESULT = typename cppcoro::awaitable_traits<AWAITABLE&&>::await_result_t,
0282             std::enable_if_t<!std::is_void_v<RESULT>, int> = 0>
0283         sync_wait_task<RESULT> make_sync_wait_task(AWAITABLE&& awaitable)
0284         {
0285             co_yield co_await std::forward<AWAITABLE>(awaitable);
0286         }
0287 
0288         template<
0289             typename AWAITABLE,
0290             typename RESULT = typename cppcoro::awaitable_traits<AWAITABLE&&>::await_result_t,
0291             std::enable_if_t<std::is_void_v<RESULT>, int> = 0>
0292         sync_wait_task<void> make_sync_wait_task(AWAITABLE&& awaitable)
0293         {
0294             co_await std::forward<AWAITABLE>(awaitable);
0295         }
0296 #endif
0297     }
0298 }
0299 
0300 #endif