• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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