Zth (libzth)
context.h
Go to the documentation of this file.
1 #ifndef ZTH_CONTEXT_H
2 #define ZTH_CONTEXT_H
3 /*
4  * Zth (libzth), a cooperative userspace multitasking library.
5  * Copyright (C) 2019-2022 Jochem Rutgers
6  *
7  * This Source Code Form is subject to the terms of the Mozilla Public
8  * License, v. 2.0. If a copy of the MPL was not distributed with this
9  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
10  */
11 
12 #include <libzth/config.h>
13 
37 #ifdef ZTH_STACK_SWITCH
38 EXTERN_C ZTH_EXPORT void*
39 zth_stack_switch(void* stack, size_t size, void* (*f)(void*) noexcept, void* arg) noexcept;
40 #else // !ZTH_STACK_SWITCH
41 # define zth_stack_switch(stack, size, f, arg) \
42  ({ \
43  (void)(stack); \
44  (void)(size); \
45  ((f)(arg)); \
46  })
47 #endif // !ZTH_STACK_SWITCH
48 
49 #ifdef __cplusplus
50 # include <libzth/util.h>
51 
52 # if __cplusplus >= 201103L
53 # include <tuple>
54 # endif
55 
56 # if __cplusplus < 201103L
57 # undef ZTH_STACK_SWITCH98
58 // Always use C++98-compatible stack_switch() implementation.
59 # define ZTH_STACK_SWITCH98 1
60 # elif !defined(ZTH_STACK_SWITCH98)
61 // By default only use C++11 stack_switch() implementation, but this can be
62 // overridden for testing purposes.
63 # define ZTH_STACK_SWITCH98 0
64 # endif
65 
66 namespace zth {
67 
68 class Context;
69 
70 struct ContextAttr {
71  typedef void* EntryArg;
72  typedef void (*Entry)(EntryArg);
73 
74  constexpr explicit ContextAttr(Entry entry = nullptr, EntryArg arg = EntryArg()) noexcept
75  : stackSize(Config::DefaultFiberStackSize)
76  , entry(entry)
77  , arg(arg)
78  {}
79 
80  size_t stackSize;
83 };
84 
85 int context_init() noexcept;
86 void context_deinit() noexcept;
87 int context_create(Context*& context, ContextAttr const& attr) noexcept;
88 void context_switch(Context* from, Context* to) noexcept;
89 void context_destroy(Context* context) noexcept;
90 size_t context_stack_usage(Context* context) noexcept;
91 
92 void stack_watermark_init(void* stack, size_t size) noexcept;
93 size_t stack_watermark_size(void* stack) noexcept;
94 size_t stack_watermark_maxused(void* stack) noexcept;
95 size_t stack_watermark_remaining(void* stack) noexcept;
96 
97 
98 
99 namespace impl {
100 
101 # if ZTH_STACK_SWITCH98
102 template <typename R>
103 struct FunctionIO0 {
104  union {
105  struct {
106  R (*f)();
107  };
108  R r;
109  };
110 
111  void* operator()()
112  {
113  r = f();
114  return &r;
115  }
116 };
117 
118 template <>
119 struct FunctionIO0<void> {
120  union {
121  struct {
122  void (*f)();
123  };
124  };
125 
126  void* operator()()
127  {
128  f();
129  return nullptr;
130  }
131 };
132 
133 template <typename R, typename A1>
134 struct FunctionIO1 {
135  union {
136  struct {
137  R (*f)(A1);
138  A1 a1;
139  };
140  R r;
141  };
142 
143  void* operator()()
144  {
145  r = f(a1);
146  return &r;
147  }
148 };
149 
150 template <typename A1>
151 struct FunctionIO1<void, A1> {
152  union {
153  struct {
154  void (*f)(A1);
155  A1 a1;
156  };
157  };
158 
159  void* operator()()
160  {
161  f(a1);
162  return nullptr;
163  }
164 };
165 
166 template <typename R, typename A1, typename A2>
167 struct FunctionIO2 {
168  union {
169  struct {
170  R (*f)(A1, A2);
171  A1 a1;
172  A2 a2;
173  };
174  R r;
175  };
176 
177  void* operator()()
178  {
179  r = f(a1, a2);
180  return &r;
181  }
182 };
183 
184 template <typename A1, typename A2>
185 struct FunctionIO2<void, A1, A2> {
186  union {
187  struct {
188  void (*f)(A1, A2);
189  A1 a1;
190  A2 a2;
191  };
192  };
193 
194  void* operator()()
195  {
196  f(a1, a2);
197  return nullptr;
198  }
199 };
200 
201 template <typename R, typename A1, typename A2, typename A3>
202 struct FunctionIO3 {
203  union {
204  struct {
205  R (*f)(A1, A2, A3);
206  A1 a1;
207  A2 a2;
208  A3 a3;
209  };
210  R r;
211  };
212  void* operator()()
213  {
214  r = f(a1, a2, a3);
215  return &r;
216  }
217 };
218 
219 template <typename A1, typename A2, typename A3>
220 struct FunctionIO3<void, A1, A2, A3> {
221  union {
222  struct {
223  void (*f)(A1, A2, A3);
224  A1 a1;
225  A2 a2;
226  A3 a3;
227  };
228  };
229 
230  void* operator()()
231  {
232  f(a1, a2, a3);
233  return nullptr;
234  }
235 };
236 
237 # elif __cplusplus >= 201103L
238 
239 template <typename T>
240 struct Packed {
241  using type = typename std::decay<T>::type;
242 };
243 
244 template <typename T>
245 struct Packed<T&> {
246  using type = std::reference_wrapper<typename std::decay<T>::type>;
247 };
248 
249 template <typename... A>
250 struct Arguments {
251  template <typename R>
252  using function_type = R(A...);
253  using tuple_type = std::tuple<typename Packed<A>::type...>;
254  constexpr static size_t size = sizeof...(A);
255 };
256 
257 template <typename R, typename A, typename A_>
258 struct FunctionION {
259  union {
260  struct {
261  typename A::template function_type<R>* f;
262  typename A_::tuple_type a;
263  };
264  typename Packed<R>::type r;
265  };
266 
267  void* operator()() noexcept
268  {
269  call(typename SequenceGenerator<A::size>::type(), A());
270  return &r;
271  }
272 
273 private:
274  template <size_t... S, typename... _A>
275  void call(Sequence<S...>, Arguments<_A...>) noexcept
276  {
277  r = f(std::forward<_A>(std::get<S>(a))...);
278  }
279 };
280 
281 template <typename A, typename A_>
282 struct FunctionION<void, A, A_> {
283  union {
284  struct {
285  typename A::template function_type<void>* f;
286  typename A_::tuple_type a;
287  };
288  };
289 
290  void* operator()() noexcept
291  {
292  call(typename SequenceGenerator<A::size>::type(), A());
293  return nullptr;
294  }
295 
296 private:
297  template <size_t... S, typename... _A>
298  void call(Sequence<S...>, Arguments<_A...>) noexcept
299  {
300  f(std::forward<_A>(std::get<S>(a))...);
301  }
302 };
303 # endif // C++11
304 
305 template <typename F>
306 void* stack_switch_fwd(void* f) noexcept
307 {
308  return (*static_cast<F*>(f))();
309 }
310 
311 } // namespace impl
312 
313 # if ZTH_STACK_SWITCH98
317 template <typename R>
318 inline R stack_switch(void* stack, size_t size, R (*f)())
319 {
320  impl::FunctionIO0<R> f_ = {{f}};
321  return *static_cast<R*>(
322  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_));
323 }
324 
330 inline void stack_switch(void* stack, size_t size, void (*f)())
331 {
332  impl::FunctionIO0<void> f_ = {{f}};
333  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_);
334 }
335 
340 template <typename R, typename A1>
341 inline R stack_switch(void* stack, size_t size, R (*f)(A1), A1 a1)
342 {
343  impl::FunctionIO1<R, A1> f_ = {{f, a1}};
344  return *static_cast<R*>(
345  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_));
346 }
347 
352 template <typename A1>
353 inline void stack_switch(void* stack, size_t size, void (*f)(A1), A1 a1)
354 {
355  impl::FunctionIO1<void, A1> f_ = {{f, a1}};
356  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_);
357 }
358 
363 template <typename R, typename A1, typename A2>
364 inline R stack_switch(void* stack, size_t size, R (*f)(A1, A2), A1 a1, A2 a2)
365 {
366  impl::FunctionIO2<R, A1, A2> f_ = {{f, a1, a2}};
367  return *static_cast<R*>(
368  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_));
369 }
370 
375 template <typename A1, typename A2>
376 inline void stack_switch(void* stack, size_t size, void (*f)(A1, A2), A1 a1, A2 a2)
377 {
378  impl::FunctionIO2<void, A1, A2> f_ = {{f, a1, a2}};
379  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_);
380 }
381 
386 template <typename R, typename A1, typename A2, typename A3>
387 inline R stack_switch(void* stack, size_t size, R (*f)(A1, A2, A3), A1 a1, A2 a2, A3 a3)
388 {
389  impl::FunctionIO3<R, A1, A2, A3> f_ = {{f, a1, a2, a3}};
390  return *static_cast<R*>(
391  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_));
392 }
393 
398 template <typename A1, typename A2, typename A3>
399 inline void stack_switch(void* stack, size_t size, void (*f)(A1, A2, A3), A1 a1, A2 a2, A3 a3)
400 {
401  impl::FunctionIO3<void, A1, A2, A3> f_ = {{f, a1, a2, a3}};
402  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_);
403 }
404 
405 # elif __cplusplus >= 201103L // !ZTH_STACK_SWITCH98
406 
412 template <typename R, typename... A, typename... A_>
413 inline typename std::enable_if<!std::is_void<R>::value, R>::type
414 stack_switch(void* stack, size_t size, R (*f)(A...) noexcept, A_&&... a) noexcept
415 {
416  impl::FunctionION<R, impl::Arguments<A...>, impl::Arguments<A_&&...>> f_{
417  {f, {std::forward<A_>(a)...}}};
418  return std::move(*static_cast<typename impl::Packed<R>::type*>(
419  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_)));
420 }
421 
427 template <typename... A, typename... A_>
428 inline void stack_switch(void* stack, size_t size, void (*f)(A...) noexcept, A_&&... a) noexcept
429 {
430  impl::FunctionION<void, impl::Arguments<A...>, impl::Arguments<A_&&...>> f_{
431  {f, {std::forward<A_>(a)...}}};
432  zth_stack_switch(stack, size, &impl::stack_switch_fwd<decltype(f_)>, &f_);
433 }
434 # endif // !ZTH_STACK_SWITCH98
435 
436 } // namespace zth
437 #endif // __cplusplus
438 #endif // ZTH_CONTEXT_H
#define zth_stack_switch(stack, size, f, arg)
Call the function f using the new stack pointer.
Definition: context.h:41
std::enable_if<!std::is_void< R >::value, R >::type stack_switch(void *stack, size_t size, R(*f)(A...) noexcept, A_ &&... a) noexcept
Call the function f using the new stack pointer.
Definition: context.h:414
size_t stack_watermark_remaining(void *stack) noexcept
Return the remaining stack size that was never touched.
Definition: context.cpp:315
void stack_watermark_init(void *stack, size_t size) noexcept
Initialize the memory region for stack high water marking.
Definition: context.cpp:245
size_t stack_watermark_maxused(void *stack) noexcept
Return the high water mark of the stack.
Definition: context.cpp:283
size_t stack_watermark_size(void *stack) noexcept
Return the size of the given stack region.
Definition: context.cpp:267
void * stack_switch_fwd(void *f) noexcept
Definition: context.h:306
Definition: allocator.h:23
void context_switch(Context *from, Context *to) noexcept
Perform context switch.
Definition: context.cpp:116
void context_destroy(Context *context) noexcept
Destroy and cleanup a context.
Definition: context.cpp:99
int context_init() noexcept
One-time context mechanism initialization.
Definition: context.cpp:32
void context_deinit() noexcept
Final cleanup.
Definition: context.cpp:43
int context_create(Context *&context, ContextAttr const &attr) noexcept
Create a context.
Definition: context.cpp:58
size_t context_stack_usage(Context *context) noexcept
Return the high water mark of the stack of the given context.
Definition: context.cpp:143
The configuration of Zth.
Definition: zth_config.h:22
EntryArg arg
Definition: context.h:82
size_t stackSize
Definition: context.h:80
void * EntryArg
Definition: context.h:71
void(* Entry)(EntryArg)
Definition: context.h:72
constexpr ContextAttr(Entry entry=nullptr, EntryArg arg=EntryArg()) noexcept
Definition: context.h:74
R(A...) function_type
Definition: context.h:252
std::tuple< typename Packed< A >::type... > tuple_type
Definition: context.h:253
A::template function_type< void > * f
Definition: context.h:285
Packed< R >::type r
Definition: context.h:264
A_::tuple_type a
Definition: context.h:262
void * operator()() noexcept
Definition: context.h:267
A::template function_type< R > * f
Definition: context.h:261
std::reference_wrapper< typename std::decay< T >::type > type
Definition: context.h:246
typename std::decay< T >::type type
Definition: context.h:241