libcopp  1.3.0
coroutine_context.cpp
Go to the documentation of this file.
1 #include <algorithm>
2 #include <assert.h>
3 #include <cstdlib>
4 #include <cstring>
5 
8 #include <libcopp/utils/errno.h>
10 
12 
13 #if defined(UTIL_CONFIG_THREAD_LOCAL)
14 // using thread_local
15 #else
16 #include <pthread.h>
17 #endif
18 
19 #ifdef LIBCOPP_MACRO_USE_SEGMENTED_STACKS
20 extern "C" {
21 void __splitstack_getcontext(void * [COPP_MACRO_SEGMENTED_STACK_NUMBER]);
22 
23 void __splitstack_setcontext(void * [COPP_MACRO_SEGMENTED_STACK_NUMBER]);
24 
25 void __splitstack_releasecontext(void * [COPP_MACRO_SEGMENTED_STACK_NUMBER]);
26 
27 void __splitstack_block_signals_context(void * [COPP_MACRO_SEGMENTED_STACK_NUMBER], int *, int *);
28 }
29 #endif
30 
31 namespace copp {
32  namespace detail {
33 
34 #if defined(LIBCOPP_DISABLE_THIS_MT) && LIBCOPP_DISABLE_THIS_MT
35  static coroutine_context *gt_current_coroutine = UTIL_CONFIG_NULLPTR;
36 #elif defined(UTIL_CONFIG_THREAD_LOCAL)
37  static UTIL_CONFIG_THREAD_LOCAL coroutine_context *gt_current_coroutine = UTIL_CONFIG_NULLPTR;
38 #else
39  static pthread_once_t gt_coroutine_init_once = PTHREAD_ONCE_INIT;
40  static pthread_key_t gt_coroutine_tls_key;
41  static void init_pthread_this_coroutine_context() { (void)pthread_key_create(&gt_coroutine_tls_key, UTIL_CONFIG_NULLPTR); }
42 #endif
43 
45 #if (defined(LIBCOPP_DISABLE_THIS_MT) && LIBCOPP_DISABLE_THIS_MT) || defined(UTIL_CONFIG_THREAD_LOCAL)
46  gt_current_coroutine = p;
47 #else
48  (void)pthread_once(&gt_coroutine_init_once, init_pthread_this_coroutine_context);
49  pthread_setspecific(gt_coroutine_tls_key, p);
50 #endif
51  }
52 
54 #if (defined(LIBCOPP_DISABLE_THIS_MT) && LIBCOPP_DISABLE_THIS_MT) || defined(UTIL_CONFIG_THREAD_LOCAL)
55  return gt_current_coroutine;
56 #else
57  (void)pthread_once(&gt_coroutine_init_once, init_pthread_this_coroutine_context);
58  return reinterpret_cast<coroutine_context *>(pthread_getspecific(gt_coroutine_tls_key));
59 #endif
60  }
61  } // namespace detail
62 
65 
67  if (UTIL_CONFIG_NULLPTR != src) {
68  src->caller_ = fctx;
69  }
70  }
71 
73  if (UTIL_CONFIG_NULLPTR != src) {
74  src->callee_ = fctx;
75  }
76  }
77 
78 #ifdef LIBCOPP_MACRO_USE_SEGMENTED_STACKS
79  static UTIL_FORCEINLINE void splitstack_swapcontext(EXPLICIT_UNUSED_ATTR stack_context &from_sctx,
82  if (UTIL_CONFIG_NULLPTR != jump_transfer.from_co) {
83  __splitstack_getcontext(jump_transfer.from_co->callee_stack_.segments_ctx);
84  if (&from_sctx != &jump_transfer.from_co->callee_stack_) {
85  memcpy(&from_sctx.segments_ctx, &jump_transfer.from_co->callee_stack_.segments_ctx, sizeof(from_sctx.segments_ctx));
86  }
87  } else {
88  __splitstack_getcontext(from_sctx.segments_ctx);
89  }
90  __splitstack_setcontext(to_sctx.segments_ctx);
91  }
92 #endif
93 
95  assert(src_ctx.data);
96  if (UTIL_CONFIG_NULLPTR == src_ctx.data) {
97  abort();
98  // return; // clang-analyzer will report "Unreachable code"
99  }
100 
101  // copy jump_src_data_t in case it's destroyed later
102  jump_src_data_t jump_src = *reinterpret_cast<jump_src_data_t *>(src_ctx.data);
103 
104  // this must in a coroutine
105  coroutine_context *ins_ptr = jump_src.to_co;
106  assert(ins_ptr);
107  if (UTIL_CONFIG_NULLPTR == ins_ptr) {
108  abort();
109  // return; // clang-analyzer will report "Unreachable code"
110  }
111 
112  // update caller of to_co
113  ins_ptr->caller_ = src_ctx.fctx;
114 
115  // save from_co's fcontext and switch status
116  if (UTIL_CONFIG_NULLPTR != jump_src.from_co) {
117  jump_src.from_co->callee_ = src_ctx.fctx;
118  }
119 
120  // this_coroutine
122 
123  // run logic code
124  ins_ptr->run_and_recv_retcode(jump_src.priv_data);
125 
127  // add memory fence to flush flags_(used in is_finished())
128  // LIBCOPP_UTIL_LOCK_ATOMIC_THREAD_FENCE(libcopp::util::lock::memory_order_release);
129 
130  // jump back to caller
131  ins_ptr->yield();
132  }
133  };
141  static inline void jump_to(fcontext::fcontext_t &to_fctx, EXPLICIT_UNUSED_ATTR stack_context &from_sctx,
143  libcopp_inner_api_helper::jump_src_data_t &jump_transfer) UTIL_CONFIG_NOEXCEPT {
144 
147  // int from_status;
148  // bool swap_success;
149  // can not use any more stack now
150  // can not initialize those vars here
151 
152 #ifdef LIBCOPP_MACRO_USE_SEGMENTED_STACKS
153  assert(&from_sctx != &to_sctx);
154  // ROOT->A: jump_transfer.from_co == UTIL_CONFIG_NULLPTR, jump_transfer.to_co == A, from_sctx == A.caller_stack_, skip backup
155  // segments A->B.start(): jump_transfer.from_co == A, jump_transfer.to_co == B, from_sctx == B.caller_stack_, backup segments
156  // B.yield()->A: jump_transfer.from_co == B, jump_transfer.to_co == UTIL_CONFIG_NULLPTR, from_sctx == B.callee_stack_, skip backup
157  // segments
158  libcopp_inner_api_helper::splitstack_swapcontext(from_sctx, to_sctx, jump_transfer);
159 #endif
160  res = copp::fcontext::copp_jump_fcontext(to_fctx, &jump_transfer);
161  if (UTIL_CONFIG_NULLPTR == res.data) {
162  abort();
163  return;
164  }
165  jump_src = reinterpret_cast<libcopp_inner_api_helper::jump_src_data_t *>(res.data);
166  assert(jump_src);
167 
182  // update caller of to_co if not jump from yield mode
184 
186 
187  // private data
188  jump_transfer.priv_data = jump_src->priv_data;
189 
190  // this_coroutine
191  detail::set_this_coroutine_context(jump_transfer.from_co);
192  }
193 
194  LIBCOPP_COPP_API coroutine_context::coroutine_context() UTIL_CONFIG_NOEXCEPT : runner_ret_code_(0),
195  flags_(0),
196  runner_(UTIL_CONFIG_NULLPTR),
197  priv_data_(UTIL_CONFIG_NULLPTR),
198  private_buffer_size_(0),
199  caller_(UTIL_CONFIG_NULLPTR),
200  callee_(UTIL_CONFIG_NULLPTR),
201  callee_stack_(),
202 #ifdef LIBCOPP_MACRO_USE_SEGMENTED_STACKS
203  caller_stack_(),
204 #endif
205  status_(status_t::EN_CRS_INVALID) {
206  }
207 
209 
211  size_t coroutine_size, size_t private_buffer_size) UTIL_CONFIG_NOEXCEPT {
212  if (UTIL_CONFIG_NULLPTR == p) {
213  return COPP_EC_ARGS_ERROR;
214  }
215 
216  // must aligned to sizeof(size_t)
217  if (0 != (private_buffer_size & (sizeof(size_t) - 1))) {
218  return COPP_EC_ARGS_ERROR;
219  }
220 
221  if (0 != (coroutine_size & (sizeof(size_t) - 1))) {
222  return COPP_EC_ARGS_ERROR;
223  }
224 
225  size_t stack_offset = private_buffer_size + coroutine_size;
226  if (UTIL_CONFIG_NULLPTR == callee_stack.sp || callee_stack.size <= stack_offset) {
227  return COPP_EC_ARGS_ERROR;
228  }
229 
230  // stack down
231  // |STACK BUFFER........COROUTINE..this..padding..PRIVATE DATA.....callee_stack.sp|
232  // |------------------------------callee_stack.size -------------------------------|
233  if (callee_stack.sp <= p || coroutine_size < sizeof(coroutine_context)) {
234  return COPP_EC_ARGS_ERROR;
235  }
236 
237  size_t this_offset = reinterpret_cast<unsigned char *>(callee_stack.sp) - reinterpret_cast<unsigned char *>(p);
238  if (this_offset < sizeof(coroutine_context) + private_buffer_size || this_offset > stack_offset) {
239  return COPP_EC_ARGS_ERROR;
240  }
241 
242  // if runner is empty, we can set it later
243  p->set_runner(COPP_MACRO_STD_MOVE(runner));
244 
245  if (&p->callee_stack_ != &callee_stack) {
246  p->callee_stack_ = callee_stack;
247  }
248  p->private_buffer_size_ = private_buffer_size;
249 
250  // stack down, left enough private data
251  p->priv_data_ = reinterpret_cast<unsigned char *>(p->callee_stack_.sp) - p->private_buffer_size_;
252  p->callee_ =
253  fcontext::copp_make_fcontext(reinterpret_cast<unsigned char *>(p->callee_stack_.sp) - stack_offset,
254  p->callee_stack_.size - stack_offset, &libcopp_inner_api_helper::coroutine_context_callback);
255  if (UTIL_CONFIG_NULLPTR == p->callee_) {
257  }
258 
259  return COPP_EC_SUCCESS;
260  }
261 
263  if (UTIL_CONFIG_NULLPTR == callee_) {
264  return COPP_EC_NOT_INITED;
265  }
266 
267  int from_status = status_t::EN_CRS_READY;
268  do {
269  if (from_status < status_t::EN_CRS_READY) {
270  return COPP_EC_NOT_INITED;
271  }
272 
273  if (status_.compare_exchange_strong(from_status, status_t::EN_CRS_RUNNING, libcopp::util::lock::memory_order_acq_rel,
275  break;
276  } else {
277  // finished or stoped
278  if (from_status > status_t::EN_CRS_RUNNING) {
279  return COPP_EC_NOT_READY;
280  }
281 
282  // already running
283  if (status_t::EN_CRS_RUNNING == from_status) {
284  return COPP_EC_IS_RUNNING;
285  }
286  }
287  } while (true);
288 
289  jump_src_data_t jump_data;
291  jump_data.to_co = this;
292  jump_data.priv_data = priv_data;
293 
294 #ifdef LIBCOPP_MACRO_USE_SEGMENTED_STACKS
295  jump_to(callee_, caller_stack_, callee_stack_, jump_data);
296 #else
297  jump_to(callee_, callee_stack_, callee_stack_, jump_data);
298 #endif
299 
300  // Move changing status to EN_CRS_EXITED is finished
301  if (check_flags(flag_t::EN_CFT_FINISHED)) {
302  // if in finished status, change it to exited
303  status_.store(status_t::EN_CRS_EXITED, libcopp::util::lock::memory_order_release);
304  }
305 
306  return COPP_EC_SUCCESS;
307  } // namespace copp
308 
309  LIBCOPP_COPP_API int coroutine_context::resume(void *priv_data) { return start(priv_data); }
310 
312  if (UTIL_CONFIG_NULLPTR == callee_) {
313  return COPP_EC_NOT_INITED;
314  }
315 
316  int from_status = status_t::EN_CRS_RUNNING;
317  int to_status = status_t::EN_CRS_READY;
318  if (check_flags(flag_t::EN_CFT_FINISHED)) {
319  to_status = status_t::EN_CRS_FINISHED;
320  }
321  if (false == status_.compare_exchange_strong(from_status, to_status, libcopp::util::lock::memory_order_acq_rel,
323  switch (from_status) {
324  case status_t::EN_CRS_INVALID:
325  return COPP_EC_NOT_INITED;
326  case status_t::EN_CRS_READY:
327  return COPP_EC_NOT_RUNNING;
328  case status_t::EN_CRS_FINISHED:
329  case status_t::EN_CRS_EXITED:
330  return COPP_EC_ALREADY_EXIST;
331  default:
332  return COPP_EC_UNKNOWN;
333  }
334  }
335 
336  // success or finished will continue
337  jump_src_data_t jump_data;
338  jump_data.from_co = this;
339  jump_data.to_co = UTIL_CONFIG_NULLPTR;
340 
341 
342 #ifdef LIBCOPP_MACRO_USE_SEGMENTED_STACKS
343  jump_to(caller_, callee_stack_, caller_stack_, jump_data);
344 #else
345  jump_to(caller_, callee_stack_, callee_stack_, jump_data);
346 #endif
347 
348  if (UTIL_CONFIG_NULLPTR != priv_data) {
349  *priv_data = jump_data.priv_data;
350  }
351 
352  return COPP_EC_SUCCESS;
353  }
354 
356  if (flags & flag_t::EN_CFT_MASK) {
357  return false;
358  }
359 
360  flags_ |= flags;
361  return true;
362  }
363 
365  if (flags & flag_t::EN_CFT_MASK) {
366  return false;
367  }
368 
369  flags_ &= ~flags;
370  return true;
371  }
372 
373  LIBCOPP_COPP_API bool coroutine_context::check_flags(int flags) const { return 0 != (flags_ & flags); }
374 
375 #if defined(UTIL_CONFIG_COMPILER_CXX_RVALUE_REFERENCES) && UTIL_CONFIG_COMPILER_CXX_RVALUE_REFERENCES
377 #else
379 #endif
380  if (!runner) {
381  return COPP_EC_ARGS_ERROR;
382  }
383 
384  int from_status = status_t::EN_CRS_INVALID;
385  if (false == status_.compare_exchange_strong(from_status, status_t::EN_CRS_READY, libcopp::util::lock::memory_order_acq_rel,
387  return COPP_EC_ALREADY_INITED;
388  }
389 
390  runner_ = COPP_MACRO_STD_MOVE(runner);
391  return COPP_EC_SUCCESS;
392  } // namespace copp
393 
395  // return !!(flags_ & flag_t::EN_CFT_FINISHED);
396  return status_.load(libcopp::util::lock::memory_order_acquire) >= status_t::EN_CRS_FINISHED;
397  }
398 
399  namespace this_coroutine {
401 
402  LIBCOPP_COPP_API int yield(void **priv_data) {
404  if (UTIL_CONFIG_NULLPTR != pco) {
405  return pco->yield(priv_data);
406  }
407 
408  return COPP_EC_NOT_RUNNING;
409  }
410  } // namespace this_coroutine
411 } // namespace copp
static coroutine_context * get_this_coroutine_context()
std::function< int(void *)> callback_t
LIBCOPP_COPP_API int start(void *priv_data=UTIL_CONFIG_NULLPTR)
start coroutine
static UTIL_FORCEINLINE void set_caller(coroutine_context *src, const fcontext::fcontext_t &fctx)
LIBCOPP_COPP_API int set_runner(const callback_t &runner)
set runner
COPP_EC_ALREADY_EXIST.
Definition: errno.h:29
导入继承关系约束 Licensed under the MIT licenses.
#define LIBCOPP_COPP_API
LIBCOPP_COPP_API coroutine_context() UTIL_CONFIG_NOEXCEPT
static UTIL_FORCEINLINE void set_callee(coroutine_context *src, const fcontext::fcontext_t &fctx)
COPP_EC_ALREADY_INITED.
Definition: errno.h:22
COPP_EC_NOT_INITED.
Definition: errno.h:21
COPP_EC_FCONTEXT_MAKE_FAILED.
Definition: errno.h:33
COPP_EC_NOT_RUNNING.
Definition: errno.h:25
static void init_pthread_this_coroutine_context()
COPP_EC_NOT_READY.
Definition: errno.h:24
COPP_EC_SUCCESS.
Definition: errno.h:12
UTIL_FORCEINLINE void run_and_recv_retcode(void *priv_data)
coroutine entrance function
LIBCOPP_COPP_API int yield(void **priv_data=UTIL_CONFIG_NULLPTR)
yield current coroutine
COPP_BOOST_CONTEXT_DECL fcontext_t COPP_BOOST_CONTEXT_CALLDECL copp_make_fcontext(void *sp, std::size_t size, void(*fn)(transfer_t))
static pthread_key_t gt_coroutine_tls_key
static LIBCOPP_COPP_API int create(coroutine_context *p, callback_t &runner, const stack_context &callee_stack, size_t coroutine_size, size_t private_buffer_size) UTIL_CONFIG_NOEXCEPT
create coroutine context at stack context callee_
coroutine_context::jump_src_data_t jump_src_data_t
#define EXPLICIT_UNUSED_ATTR
maybe_unused, 标记忽略unused警告 usage: EXPLICIT_UNUSED_ATTR int a; class EXPLICIT_UNUSED_ATTR a; EXP...
COPP_EC_IS_RUNNING.
Definition: errno.h:26
#define UTIL_FORCEINLINE
LIBCOPP_COPP_API ~coroutine_context()
LIBCOPP_COPP_API bool unset_flags(int flags)
set all flags to false
static void jump_to(fcontext::fcontext_t &to_fctx, EXPLICIT_UNUSED_ATTR stack_context &from_sctx, EXPLICIT_UNUSED_ATTR stack_context &to_sctx, libcopp_inner_api_helper::jump_src_data_t &jump_transfer) UTIL_CONFIG_NOEXCEPT
call platform jump to asm instruction
#define COPP_MACRO_STD_MOVE(x)
Definition: features.h:171
static void set_this_coroutine_context(coroutine_context *p)
LIBCOPP_COPP_API int yield(void **priv_data=UTIL_CONFIG_NULLPTR)
yield coroutine
LIBCOPP_COPP_API int resume(void *priv_data=UTIL_CONFIG_NULLPTR)
resume coroutine
static pthread_once_t gt_coroutine_init_once
COPP_BOOST_CONTEXT_DECL transfer_t COPP_BOOST_CONTEXT_CALLDECL copp_jump_fcontext(fcontext_t const to, void *vp)
fcontext::fcontext_t callee_
LIBCOPP_COPP_API coroutine_context * get_coroutine() UTIL_CONFIG_NOEXCEPT
get current coroutine
LIBCOPP_COPP_API bool check_flags(int flags) const
check flags
static UTIL_SYMBOL_HIDDEN void coroutine_context_callback(::copp::fcontext::transfer_t src_ctx)
COPP_EC_UNKNOWN.
Definition: errno.h:14
#define UTIL_SYMBOL_HIDDEN
base type of all coroutine context
LIBCOPP_COPP_API bool set_flags(int flags)
set all flags to true
fcontext::fcontext_t caller_
COPP_EC_ARGS_ERROR.
Definition: errno.h:30
LIBCOPP_COPP_API bool is_finished() const UTIL_CONFIG_NOEXCEPT
get runner return code