1 // Copyright (c) 2016-2018 Hartmut Kaiser 2 // 3 // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 #if !defined(HPX_THREADS_RUN_AS_HPX_THREAD_MAR_12_2016_0202PM) 7 #define HPX_THREADS_RUN_AS_HPX_THREAD_MAR_12_2016_0202PM 8 9 #include <hpx/config.hpp> 10 #include <hpx/lcos/local/spinlock.hpp> 11 #include <hpx/runtime/threads/thread_helpers.hpp> 12 #include <hpx/util/assert.hpp> 13 #include <hpx/util/invoke_fused.hpp> 14 #include <hpx/util/optional.hpp> 15 #include <hpx/util/result_of.hpp> 16 #include <hpx/util/tuple.hpp> 17 18 #include <chrono> 19 #include <condition_variable> 20 #include <cstdlib> 21 #include <exception> 22 #include <functional> 23 #include <mutex> 24 #include <thread> 25 #include <type_traits> 26 #include <utility> 27 28 namespace hpx { namespace threads 29 { 30 /////////////////////////////////////////////////////////////////////////// 31 namespace detail 32 { 33 // This is the overload for running functions which return a value. 34 template <typename F, typename... Ts> 35 typename util::invoke_result<F, Ts...>::type run_as_hpx_thread(std::false_type,F const & f,Ts &&...ts)36 run_as_hpx_thread(std::false_type, F const& f, Ts &&... ts) 37 { 38 hpx::lcos::local::spinlock mtx; 39 std::condition_variable_any cond; 40 bool stopping = false; 41 42 typedef typename util::invoke_result<F, Ts...>::type result_type; 43 44 // Using the optional for storing the returned result value 45 // allows to support non-default-constructible and move-only 46 // types. 47 hpx::util::optional<result_type> result; 48 std::exception_ptr exception; 49 50 // This lambda function will be scheduled to run as an HPX 51 // thread 52 auto && args = util::forward_as_tuple(std::forward<Ts>(ts)...); 53 auto && wrapper = 54 [&]() mutable 55 { 56 try 57 { 58 // Execute the given function, forward all parameters, 59 // store result. 60 result.emplace(util::invoke_fused(f, std::move(args))); 61 } 62 catch (...) 63 { 64 // make sure exceptions do not escape the HPX thread 65 // scheduler 66 exception = std::current_exception(); 67 } 68 69 // Now signal to the waiting thread that we're done. 70 { 71 std::lock_guard<hpx::lcos::local::spinlock> lk(mtx); 72 stopping = true; 73 } 74 cond.notify_all(); 75 }; 76 77 // Create the HPX thread 78 hpx::threads::register_thread_nullary(std::ref(wrapper)); 79 80 // wait for the HPX thread to exit 81 std::unique_lock<hpx::lcos::local::spinlock> lk(mtx); 82 while (!stopping) 83 cond.wait(lk); 84 85 // rethrow exceptions 86 if (exception) 87 { 88 std::rethrow_exception(exception); 89 } 90 91 return std::move(*result); 92 } 93 94 // This is the overload for running functions which return void. 95 template <typename F, typename... Ts> run_as_hpx_thread(std::true_type,F const & f,Ts &&...ts)96 void run_as_hpx_thread(std::true_type, F const& f, Ts &&... ts) 97 { 98 hpx::lcos::local::spinlock mtx; 99 std::condition_variable_any cond; 100 bool stopping = false; 101 102 std::exception_ptr exception; 103 104 // This lambda function will be scheduled to run as an HPX 105 // thread 106 auto && args = util::forward_as_tuple(std::forward<Ts>(ts)...); 107 auto && wrapper = 108 [&]() mutable 109 { 110 try 111 { 112 // Execute the given function, forward all parameters. 113 util::invoke_fused(f, std::move(args)); 114 } 115 catch (...) 116 { 117 // make sure exceptions do not escape the HPX thread 118 // scheduler 119 exception = std::current_exception(); 120 } 121 122 // Now signal to the waiting thread that we're done. 123 { 124 std::lock_guard<hpx::lcos::local::spinlock> lk(mtx); 125 stopping = true; 126 } 127 cond.notify_all(); 128 }; 129 130 // Create an HPX thread 131 hpx::threads::register_thread_nullary(std::ref(wrapper)); 132 133 // wait for the HPX thread to exit 134 std::unique_lock<hpx::lcos::local::spinlock> lk(mtx); 135 while (!stopping) 136 cond.wait(lk); 137 138 // rethrow exceptions 139 if (exception) 140 { 141 std::rethrow_exception(exception); 142 } 143 } 144 } 145 146 /////////////////////////////////////////////////////////////////////////// 147 template <typename F, typename... Ts> 148 typename util::invoke_result<F, Ts...>::type run_as_hpx_thread(F const & f,Ts &&...vs)149 run_as_hpx_thread(F const& f, Ts &&... vs) 150 { 151 // This shouldn't be used on a HPX-thread 152 HPX_ASSERT(hpx::threads::get_self_ptr() == nullptr); 153 154 typedef typename std::is_void< 155 typename util::invoke_result<F, Ts...>::type 156 >::type result_is_void; 157 158 return detail::run_as_hpx_thread(result_is_void(), 159 f, std::forward<Ts>(vs)...); 160 } 161 }} 162 163 #endif 164