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> call(F && f,Args &&...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> call(F && f,Args &&...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