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
26namespace marl {
27namespace detail {
28
29template <typename RETURN_TYPE>
30class 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
55template <>
56class 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// }
97template <typename F, typename... Args>
98MARL_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