Seastar
High performance C++ framework for concurrent servers
thread.hh
1 /*
2  * This file is open source software, licensed to you under the terms
3  * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4  * distributed with this work for additional information regarding copyright
5  * ownership. You may not use this file except in compliance with the License.
6  *
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an
13  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14  * KIND, either express or implied. See the License for the
15  * specific language governing permissions and limitations
16  * under the License.
17  */
18 
19 /*
20  * Copyright (C) 2015 Cloudius Systems, Ltd.
21  */
22 
23 #pragma once
24 
25 #ifndef SEASTAR_MODULE
26 #include <seastar/core/thread_impl.hh>
27 #include <seastar/core/future.hh>
28 #include <seastar/core/do_with.hh>
29 #include <seastar/core/timer.hh>
31 #include <memory>
32 #include <setjmp.h>
33 #include <type_traits>
34 #include <chrono>
35 #include <seastar/util/std-compat.hh>
36 #include <seastar/util/modules.hh>
37 #include <ucontext.h>
38 #include <boost/intrusive/list.hpp>
39 #endif
40 
70 
72 namespace seastar {
73 
76 SEASTAR_MODULE_EXPORT_BEGIN
77 class thread;
78 class thread_attributes;
79 
82 public:
83  std::optional<seastar::scheduling_group> sched_group;
84  // For stack_size 0, a default value will be used (128KiB when writing this comment)
85  size_t stack_size = 0;
86 };
87 SEASTAR_MODULE_EXPORT_END
88 
90 extern thread_local jmp_buf_link g_unthreaded_context;
91 
92 // Internal class holding thread state. We can't hold this in
93 // \c thread itself because \c thread is movable, and we want pointers
94 // to this state to be captured.
95 class thread_context final : private task {
96  struct stack_deleter {
97  void operator()(char *ptr) const noexcept;
98  int valgrind_id;
99  stack_deleter(int valgrind_id);
100  };
101  using stack_holder = std::unique_ptr<char[], stack_deleter>;
102 
103  stack_holder _stack;
104  noncopyable_function<void ()> _func;
105  jmp_buf_link _context;
106  promise<> _done;
107  bool _joined = false;
108 
109  boost::intrusive::list_member_hook<> _all_link;
110  using all_thread_list = boost::intrusive::list<thread_context,
111  boost::intrusive::member_hook<thread_context, boost::intrusive::list_member_hook<>,
112  &thread_context::_all_link>,
113  boost::intrusive::constant_time_size<false>>;
114 
115  static thread_local all_thread_list _all_threads;
116 private:
117  static void s_main(int lo, int hi); // all parameters MUST be 'int' for makecontext
118  void setup(size_t stack_size);
119  void main();
120  stack_holder make_stack(size_t stack_size);
121  virtual void run_and_dispose() noexcept override; // from task class
122 public:
123  thread_context(thread_attributes attr, noncopyable_function<void ()> func);
124  ~thread_context();
125  void switch_in();
126  void switch_out();
127  bool should_yield() const;
128  void reschedule();
129  void yield();
130  task* waiting_task() noexcept override { return _done.waiting_task(); }
131  friend class thread;
132  friend void thread_impl::switch_in(thread_context*);
133  friend void thread_impl::switch_out(thread_context*);
134  friend scheduling_group thread_impl::sched_group(const thread_context*);
135 };
136 
138 
139 SEASTAR_MODULE_EXPORT
146 class thread {
147  std::unique_ptr<thread_context> _context;
148  static thread_local thread* _current;
149 public:
152  thread() = default;
157  template <typename Func>
158  thread(Func func);
164  template <typename Func>
165  thread(thread_attributes attr, Func func);
167  thread(thread&& x) noexcept = default;
169  thread& operator=(thread&& x) noexcept = default;
173  ~thread() { assert(!_context || _context->_joined); }
178  future<> join();
183  static void yield();
188  static bool should_yield();
189 
194  static void maybe_yield();
195 
196  static bool running_in_thread() {
197  return thread_impl::get() != nullptr;
198  }
199 };
200 
201 template <typename Func>
202 inline
204  : _context(std::make_unique<thread_context>(std::move(attr), std::move(func))) {
205 }
206 
207 template <typename Func>
208 inline
209 thread::thread(Func func)
210  : thread(thread_attributes(), std::move(func)) {
211 }
212 
213 inline
214 future<>
216  _context->_joined = true;
217  return _context->_done.get_future();
218 }
219 
220 SEASTAR_MODULE_EXPORT_BEGIN
244 template <typename Func, typename... Args>
245 inline
246 futurize_t<std::invoke_result_t<Func, Args...>>
247 async(thread_attributes attr, Func&& func, Args&&... args) noexcept {
248  using return_type = std::invoke_result_t<Func, Args...>;
249  struct work {
250  thread_attributes attr;
251  Func func;
252  std::tuple<Args...> args;
254  thread th{};
255  };
256 
257  try {
258  auto wp = std::make_unique<work>(work{std::move(attr), std::forward<Func>(func), std::forward_as_tuple(std::forward<Args>(args)...)});
259  auto& w = *wp;
260  auto ret = w.pr.get_future();
261  w.th = thread(std::move(w.attr), [&w] {
262  futurize<return_type>::apply(std::move(w.func), std::move(w.args)).forward_to(std::move(w.pr));
263  });
264  return w.th.join().then([ret = std::move(ret)] () mutable {
265  return std::move(ret);
266  }).finally([wp = std::move(wp)] {});
267  } catch (...) {
268  return futurize<return_type>::make_exception_future(std::current_exception());
269  }
270 }
271 
281 template <typename Func, typename... Args>
282 inline
283 futurize_t<std::invoke_result_t<Func, Args...>>
284 async(Func&& func, Args&&... args) noexcept {
285  return async(thread_attributes{}, std::forward<Func>(func), std::forward<Args>(args)...);
286 }
288 
289 SEASTAR_MODULE_EXPORT_END
290 }
Definition: task.hh:35
thread - stateful thread of execution
Definition: thread.hh:146
~thread()
Destroys a thread object.
Definition: thread.hh:173
thread(thread &&x) noexcept=default
Moves a thread object.
thread & operator=(thread &&x) noexcept=default
Move-assigns a thread object.
static void maybe_yield()
Yield if this thread ought to call yield() now.
static void yield()
Voluntarily defer execution of current thread.
thread()=default
Constructs a thread object that does not represent a thread of execution.
static bool should_yield()
Checks whether this thread ought to call yield() now.
future yield() noexcept
Returns a future which is not ready but is scheduled to resolve soon.
Class that holds attributes controling the behavior of a thread.
Definition: thread.hh:81
future join()
Waits for thread execution to terminate.
Definition: thread.hh:215
futurize_t< std::invoke_result_t< Func, Args... > > async(thread_attributes attr, Func &&func, Args &&... args) noexcept
Definition: thread.hh:247
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Converts a type to a future type, if it isn't already.
Definition: future.hh:1843