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