Lely core libraries 2.3.4
fiber.hpp
Go to the documentation of this file.
1// NOLINT(legal/copyright)
39#ifndef LELY_UTIL_FIBER_HPP_
40#define LELY_UTIL_FIBER_HPP_
41
43#include <lely/util/error.hpp>
44#include <lely/util/fiber.h>
45
46#include <utility>
47
48namespace lely {
49namespace util {
50
80
81constexpr FiberFlag
82operator~(FiberFlag rhs) {
83 return static_cast<FiberFlag>(~static_cast<int>(rhs));
84}
85
86constexpr FiberFlag
87operator&(FiberFlag lhs, FiberFlag rhs) {
88 return static_cast<FiberFlag>(static_cast<int>(lhs) & static_cast<int>(rhs));
89}
90
91constexpr FiberFlag
92operator^(FiberFlag lhs, FiberFlag rhs) {
93 return static_cast<FiberFlag>(static_cast<int>(lhs) ^ static_cast<int>(rhs));
94}
95
96constexpr FiberFlag
97operator|(FiberFlag lhs, FiberFlag rhs) {
98 return static_cast<FiberFlag>(static_cast<int>(lhs) | static_cast<int>(rhs));
99}
100
101inline FiberFlag&
102operator&=(FiberFlag& lhs, FiberFlag rhs) {
103 return lhs = lhs & rhs;
104}
105
106inline FiberFlag&
107operator^=(FiberFlag& lhs, FiberFlag rhs) {
108 return lhs = lhs ^ rhs;
109}
110
111inline FiberFlag&
112operator|=(FiberFlag& lhs, FiberFlag rhs) {
113 return lhs = lhs | rhs;
114}
115
116#if __cpp_exceptions
117
118class Fiber;
119
120namespace detail {
121
122struct FiberData {
123 bool terminated{false};
124 bool unwind{false};
125 ::std::exception_ptr eptr{nullptr};
126};
127
128} // namespace detail
129
135class FiberThread {
136 friend class Fiber;
137
138 public:
140 FiberThread() : FiberThread(static_cast<FiberFlag>(0)) {}
141
149 explicit FiberThread(FiberFlag flags) {
150 if (fiber_thrd_init(static_cast<int>(flags)) == -1)
151 throw_errc("FiberThread");
152 }
153
164 FiberThread(FiberFlag flags, bool& already) {
165 int result = fiber_thrd_init(static_cast<int>(flags));
166 if (result == -1) throw_errc("FiberThread");
167 already = result != 0;
168 }
169
170 FiberThread(const FiberThread&) = delete;
171 FiberThread(FiberThread&&) = delete;
172
173 FiberThread& operator=(const FiberThread&) = delete;
174 FiberThread& operator=(FiberThread&&) = delete;
175
180 ~FiberThread() { fiber_thrd_fini(); }
181
182 private:
183 static detail::FiberData*
184 data_() noexcept {
185#if LELY_NO_THREADS
186 static detail::FiberData data_;
187#else
188 static thread_local detail::FiberData data_;
189#endif
190 return &data_;
191 }
192};
193
195class Fiber {
196 public:
202 Fiber() noexcept = default;
203
204 Fiber(const Fiber&) = delete;
205
211 Fiber(Fiber&& other) noexcept { swap(other); }
212
213 explicit Fiber(fiber_t* fiber) noexcept : fiber_(fiber) {}
214
216 template <class F, class = typename ::std::enable_if<!::std::is_same<
217 typename ::std::decay<F>::type, Fiber>::value>::type>
218 explicit Fiber(F&& f) : Fiber(::std::forward<F>(f), 0) {}
219
221 template <class F>
222 Fiber(F&& f, FiberFlag flags) : Fiber(::std::forward<F>(f), flags, 0) {}
223
225 template <class F>
226 Fiber(F&& f, ::std::size_t stack_size)
227 : Fiber(::std::forward<F>(f), static_cast<FiberFlag>(0), stack_size) {}
228
242 template <class F, class = typename ::std::enable_if<compat::is_invocable_r<
243 Fiber, F, Fiber&&>::value>::type>
244 Fiber(F&& f, FiberFlag flags, ::std::size_t stack_size) {
245 fiber_ = fiber_create(
246 &func_<decltype(f)>, static_cast<void*>(::std::addressof(f)),
247 static_cast<int>(flags), sizeof(detail::FiberData), stack_size);
248 if (!fiber_) throw_errc("Fiber");
249 auto data = data_(fiber_);
250 // The default constructor for detail::FiberData does not throw.
251 new (data) detail::FiberData();
252 // The first call to func_() is guaranteed to not throw.
253 *this = ::std::move(*this).resume();
254 // Handle the exception thrown by the fiber.
255 auto eptr = data->eptr;
256 if (eptr) {
257 this->~Fiber();
258 ::std::rethrow_exception(eptr);
259 }
260 }
261
262 Fiber& operator=(const Fiber&) = delete;
263
271 Fiber&
272 operator=(Fiber&& other) noexcept {
273 swap(other);
274 return *this;
275 }
276
291 ~Fiber() {
292 if (fiber_) {
293 auto data = data_(fiber_);
294 if (data) {
295 if (!data->terminated) {
296 data->unwind = true;
297 *this = ::std::move(*this).resume();
298 }
299 data->~FiberData();
300 fiber_destroy(fiber_);
301 }
302 }
303 }
304
306 explicit operator bool() const noexcept { return fiber_ != nullptr; }
307
308 operator fiber_t*() && noexcept {
309 fiber_t* tmp = fiber_;
310 fiber_ = nullptr;
311 return tmp;
312 }
313
327 Fiber
328 resume() && {
329 return resume_(Fiber(fiber_resume(::std::move(*this))));
330 }
331
346 template <class F>
347 Fiber
348 resume_with(F&& f) && {
349 auto func = [](fiber_t* fiber, void* arg) noexcept {
350 Fiber f(fiber);
351 try {
352 f = (*static_cast<F*>(arg))(::std::move(f));
353 } catch (...) {
354 auto data = data_();
355 if (!data) data = FiberThread::data_();
356 data->eptr = ::std::current_exception();
357 }
358 return static_cast<fiber_t*>(::std::move(f));
359 };
360 auto arg = static_cast<void*>(::std::addressof(f));
361 return resume_(Fiber(fiber_resume_with(::std::move(*this), func, arg)));
362 }
363
365 void
366 swap(Fiber& other) noexcept {
367 using ::std::swap;
368 swap(fiber_, other.fiber_);
369 }
370
371 private:
372 template <class F>
373 static fiber_t* func_(fiber_t* fiber, void* arg) noexcept;
374
375 static detail::FiberData*
376 data_(fiber_t* fiber = nullptr) noexcept {
377 return static_cast<detail::FiberData*>(fiber_data(fiber));
378 }
379
380 Fiber resume_(Fiber&& f);
381
382 fiber_t* fiber_{nullptr};
383};
384
389class fiber_unwind {
390 friend class Fiber;
391
392 public:
393 fiber_unwind() noexcept = default;
394
395 explicit fiber_unwind(Fiber&& f) noexcept : f_(::std::move(f)) {}
396
397 private:
398 Fiber f_;
399};
400
401template <class F>
402fiber_t*
403Fiber::func_(fiber_t* fiber, void* arg) noexcept {
404 auto data = data_();
405 try {
406 // Copy the function to be executed to the stack for later reference.
407 using F_ = typename ::std::decay<F>::type;
408 F_ func{::std::forward<F_>(static_cast<F>(*static_cast<F_*>(arg)))};
409 // Return to the constructor.
410 Fiber f(fiber_resume(fiber));
411 fiber = nullptr;
412 try {
413 // Rethrow the exception thrown the function passed to resume_with(), if
414 // any.
415 if (data->eptr) ::std::rethrow_exception(data->eptr);
416 // Invoke the function, unless the fiber is being destroyed.
417 if (!data->unwind) f = func(::std::move(f));
418 } catch (fiber_unwind& e) {
419 f = ::std::move(e.f_);
420 }
421 data->terminated = true;
422 return ::std::move(f);
423 } catch (...) {
424 if (fiber) {
425 // An exception was thrown while copying the function. Store it and return
426 // to the constructor of the fiber.
427 data->eptr = ::std::current_exception();
428 return fiber;
429 }
430 // Similar to threads, exceptions thrown by a fiber function will result in
431 // termination.
432 ::std::terminate();
433 }
434}
435
436inline Fiber
437Fiber::resume_(Fiber&& f) {
438 auto data = data_();
439 if (!data) data = FiberThread::data_();
440 if (data) {
441 // Store the exception thrown by the function passed to resume_with(), if
442 // any, and clear it, so it does not get thrown again on the next call to
443 // resume().
444 auto eptr = data->eptr;
445 data->eptr = nullptr;
446 // Termination takes precedence over any exception.
447 if (data->unwind) throw fiber_unwind(::std::move(f));
448 if (eptr) ::std::rethrow_exception(eptr);
449 }
450 return ::std::move(f);
451}
452
453#endif // __cpp_exceptions
454
455} // namespace util
456} // namespace lely
457
458#endif // !LELY_UTIL_FIBER_HPP_
A CANopen value.
Definition val.hpp:42
This header file is part of the utilities library; it contains C++ convenience functions for creating...
void throw_errc(int errc=get_errc())
Throws an std::system_error exception corresponding to the specified or current (thread-specific) nat...
Definition error.hpp:73
This header file is part of the utilities library; it contains the fiber declarations.
#define FIBER_SAVE_MASK
A flag specifying a fiber to save and restore the signal mask (only supported on POSIX platforms).
Definition fiber.h:47
fiber_t * fiber_resume_with(fiber_t *fiber, fiber_func_t *func, void *arg)
Suspends the calling fiber and resumes the specified fiber, optionally executing a function before re...
Definition fiber.c:430
#define FIBER_GUARD_STACK
A flag specifying a fiber to add a guard page when allocating the stack frame so that the kernel gene...
Definition fiber.h:85
void * fiber_data(const fiber_t *fiber)
Returns a pointer to the data region of the specified fiber, or of the calling fiber if fiber is NULL...
Definition fiber.c:416
int fiber_thrd_init(int flags)
Initializes the fiber associated with the calling thread.
Definition fiber.c:210
#define FIBER_SAVE_ERROR
A flag specifying a fiber to save and restore the error values (i.e., errno and GetLastError() on Win...
Definition fiber.h:58
fiber_t * fiber_create(fiber_func_t *func, void *arg, int flags, size_t data_size, size_t stack_size)
Creates a new fiber, allocates a stack and sets up a calling environment to begin executing the speci...
Definition fiber.c:260
void fiber_thrd_fini(void)
Finalizes the fiber associated with the calling thread.
Definition fiber.c:242
#define FIBER_SAVE_FENV
A flag specifying a fiber to save and restore the floating-point environment.
Definition fiber.h:52
#define FIBER_SAVE_ALL
A combination of those flags in FIBER_SAVE_MASK, FIBER_SAVE_FENV and FIBER_SAVE_ERROR that are suppor...
Definition fiber.h:68
fiber_t * fiber_resume(fiber_t *fiber)
Equivalent to fiber_resume_with(fiber, NULL, NULL).
Definition fiber.c:424
void fiber_destroy(fiber_t *fiber)
Destroys the specified fiber.
Definition fiber.c:395
FiberFlag
Specifies which properties of the calling environment are saved or restored by a fiber when it is sus...
Definition fiber.hpp:55
@ SAVE_MASK
The fiber saves and restores the signal mask (only supported on POSIX platforms).
@ SAVE_ALL
The combination of FiberFlag::SAVE_MASK, FiberFlag::SAVE_FENV and FiberFlag::SAVE_ERROR that is suppo...
@ SAVE_FENV
The fiber saves and restores the floating-point environment.
@ GUARD_STACK
The fiber adds a guard page when allocating the stack frame so that the kernel generates a SIGSEGV si...
@ SAVE_ERROR
The fiber saves and restores the error values (i.e., errno and GetLastError() on Windows).
This header file is part of the compatibility library; it includes <type_traits> and defines any miss...
STL namespace.
A fiber.
Definition fiber.c:130