| // Copyright 2019 The Marl Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef marl_blocking_call_h |
| #define marl_blocking_call_h |
| |
| #include "export.h" |
| #include "scheduler.h" |
| #include "waitgroup.h" |
| |
| #include <thread> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace marl { |
| namespace detail { |
| |
| template <typename RETURN_TYPE> |
| class OnNewThread { |
| public: |
| template <typename F, typename... Args> |
| MARL_NO_EXPORT inline static RETURN_TYPE call(F&& f, Args&&... args) { |
| RETURN_TYPE result; |
| WaitGroup wg(1); |
| auto scheduler = Scheduler::get(); |
| auto thread = std::thread( |
| [&, wg](Args&&... args) { |
| if (scheduler != nullptr) { |
| scheduler->bind(); |
| } |
| result = f(std::forward<Args>(args)...); |
| if (scheduler != nullptr) { |
| Scheduler::unbind(); |
| } |
| wg.done(); |
| }, |
| std::forward<Args>(args)...); |
| wg.wait(); |
| thread.join(); |
| return result; |
| } |
| }; |
| |
| template <> |
| class OnNewThread<void> { |
| public: |
| template <typename F, typename... Args> |
| MARL_NO_EXPORT inline static void call(F&& f, Args&&... args) { |
| WaitGroup wg(1); |
| auto scheduler = Scheduler::get(); |
| auto thread = std::thread( |
| [&, wg](Args&&... args) { |
| if (scheduler != nullptr) { |
| scheduler->bind(); |
| } |
| f(std::forward<Args>(args)...); |
| if (scheduler != nullptr) { |
| Scheduler::unbind(); |
| } |
| wg.done(); |
| }, |
| std::forward<Args>(args)...); |
| wg.wait(); |
| thread.join(); |
| } |
| }; |
| |
| } // namespace detail |
| |
| // blocking_call() calls the function F on a new thread, yielding this fiber |
| // to execute other tasks until F has returned. |
| // |
| // Example: |
| // |
| // void runABlockingFunctionOnATask() |
| // { |
| // // Schedule a task that calls a blocking, non-yielding function. |
| // marl::schedule([=] { |
| // // call_blocking_function() may block indefinitely. |
| // // Ensure this call does not block other tasks from running. |
| // auto result = marl::blocking_call(call_blocking_function); |
| // // call_blocking_function() has now returned. |
| // // result holds the return value of the blocking function call. |
| // }); |
| // } |
| template <typename F, typename... Args> |
| MARL_NO_EXPORT auto inline blocking_call(F&& f, Args&&... args) |
| -> decltype(f(args...)) { |
| return detail::OnNewThread<decltype(f(args...))>::call( |
| std::forward<F>(f), std::forward<Args>(args)...); |
| } |
| |
| } // namespace marl |
| |
| #endif // marl_blocking_call_h |