1 | // Copyright 2019 The Marl Authors. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #ifndef marl_blocking_call_h |
16 | #define marl_blocking_call_h |
17 | |
18 | #include "export.h" |
19 | #include "scheduler.h" |
20 | #include "waitgroup.h" |
21 | |
22 | #include <thread> |
23 | #include <type_traits> |
24 | #include <utility> |
25 | |
26 | namespace marl { |
27 | namespace detail { |
28 | |
29 | template <typename RETURN_TYPE> |
30 | class OnNewThread { |
31 | public: |
32 | template <typename F, typename... Args> |
33 | MARL_NO_EXPORT inline static RETURN_TYPE call(F&& f, Args&&... args) { |
34 | RETURN_TYPE result; |
35 | WaitGroup wg(1); |
36 | auto scheduler = Scheduler::get(); |
37 | auto thread = std::thread( |
38 | [&, wg](Args&&... args) { |
39 | if (scheduler != nullptr) { |
40 | scheduler->bind(); |
41 | } |
42 | result = f(std::forward<Args>(args)...); |
43 | if (scheduler != nullptr) { |
44 | Scheduler::unbind(); |
45 | } |
46 | wg.done(); |
47 | }, |
48 | std::forward<Args>(args)...); |
49 | wg.wait(); |
50 | thread.join(); |
51 | return result; |
52 | } |
53 | }; |
54 | |
55 | template <> |
56 | class OnNewThread<void> { |
57 | public: |
58 | template <typename F, typename... Args> |
59 | MARL_NO_EXPORT inline static void call(F&& f, Args&&... args) { |
60 | WaitGroup wg(1); |
61 | auto scheduler = Scheduler::get(); |
62 | auto thread = std::thread( |
63 | [&, wg](Args&&... args) { |
64 | if (scheduler != nullptr) { |
65 | scheduler->bind(); |
66 | } |
67 | f(std::forward<Args>(args)...); |
68 | if (scheduler != nullptr) { |
69 | Scheduler::unbind(); |
70 | } |
71 | wg.done(); |
72 | }, |
73 | std::forward<Args>(args)...); |
74 | wg.wait(); |
75 | thread.join(); |
76 | } |
77 | }; |
78 | |
79 | } // namespace detail |
80 | |
81 | // blocking_call() calls the function F on a new thread, yielding this fiber |
82 | // to execute other tasks until F has returned. |
83 | // |
84 | // Example: |
85 | // |
86 | // void runABlockingFunctionOnATask() |
87 | // { |
88 | // // Schedule a task that calls a blocking, non-yielding function. |
89 | // marl::schedule([=] { |
90 | // // call_blocking_function() may block indefinitely. |
91 | // // Ensure this call does not block other tasks from running. |
92 | // auto result = marl::blocking_call(call_blocking_function); |
93 | // // call_blocking_function() has now returned. |
94 | // // result holds the return value of the blocking function call. |
95 | // }); |
96 | // } |
97 | template <typename F, typename... Args> |
98 | MARL_NO_EXPORT auto inline blocking_call(F&& f, Args&&... args) |
99 | -> decltype(f(args...)) { |
100 | return detail::OnNewThread<decltype(f(args...))>::call( |
101 | std::forward<F>(f), std::forward<Args>(args)...); |
102 | } |
103 | |
104 | } // namespace marl |
105 | |
106 | #endif // marl_blocking_call_h |
107 | |