Beispiel #1
0
/*
 *  call-seq:
 *     fiber.transfer(args, ...) -> obj
 *
 *  Transfers control to receiver fiber of the method call.
 *  Unlike <code>resume</code> the receiver wouldn't be pushed to call
 * stack of fibers. Instead it will switch to the call stack of
 * transferring fiber.
 *  When resuming a fiber that was transferred to another fiber it would
 * cause double resume error. Though when the fiber is re-transferred
 * and <code>Fiber.yield</code> is called, the fiber would be resumable.
 */
static mrb_value
fiber_transfer(mrb_state *mrb, mrb_value self)
{
  struct mrb_context *c = fiber_check(mrb, self);
  mrb_value* a;
  mrb_int len;

  fiber_check_cfunc(mrb, mrb->c);
  mrb_get_args(mrb, "*", &a, &len);

  if (c == mrb->root_c) {
    mrb->c->status = MRB_FIBER_TRANSFERRED;
    mrb->c = c;
    c->status = MRB_FIBER_RUNNING;
    MARK_CONTEXT_MODIFY(c);
    mrb_write_barrier(mrb, (struct RBasic*)c->fib);
    return fiber_result(mrb, a, len);
  }

  if (c == mrb->c) {
    return fiber_result(mrb, a, len);
  }

  return fiber_switch(mrb, self, len, a, FALSE, FALSE);
}
Beispiel #2
0
void fiber_exit(int exit_code)
{
	fiber_check();

	__thread_fiber->exitcode = exit_code;
	__thread_fiber->running->status = FIBER_STATUS_EXITING;

	acl_fiber_switch();
}
Beispiel #3
0
/* see /usr/include/bits/errno.h for __errno_location */
int *__errno_location(void)
{
	if (!acl_var_hook_sys_api)
		return __sys_errno();

	if (__thread_fiber == NULL)
		fiber_check();

	if (__thread_fiber->running)
		return &__thread_fiber->running->errnum;
	else
		return &__thread_fiber->original.errnum;
}
Beispiel #4
0
/*
 *  call-seq:
 *     fiber.resume(args, ...) -> obj
 *
 *  Resumes the fiber from the point at which the last <code>Fiber.yield</code>
 *  was called, or starts running it if it is the first call to
 *  <code>resume</code>. Arguments passed to resume will be the value of
 *  the <code>Fiber.yield</code> expression or will be passed as block
 *  parameters to the fiber's block if this is the first <code>resume</code>.
 *
 *  Alternatively, when resume is called it evaluates to the arguments passed
 *  to the next <code>Fiber.yield</code> statement inside the fiber's block
 *  or to the block value if it runs to completion without any
 *  <code>Fiber.yield</code>
 */
static mrb_value
fiber_resume(mrb_state *mrb, mrb_value self)
{
  struct mrb_context *c = fiber_check(mrb, self);
  mrb_value *a;
  int len;
  mrb_callinfo *ci;

  for (ci = c->ci; ci >= c->cibase; ci--) {
    if (ci->acc < 0) {
      mrb_raise(mrb, E_ARGUMENT_ERROR, "can't cross C function boundary");
    }
  }
  if (c->status == MRB_FIBER_RESUMED) {
    mrb_raise(mrb, E_RUNTIME_ERROR, "double resume");
  }
  if (c->status == MRB_FIBER_TERMINATED) {
    mrb_raise(mrb, E_RUNTIME_ERROR, "resuming dead fiber");
  }
  mrb_get_args(mrb, "*", &a, &len);
  mrb->c->status = MRB_FIBER_RESUMED;
  if (c->status == MRB_FIBER_CREATED) {
    mrb_value *b = c->stack+1;
    mrb_value *e = b + len;

    while (b<e) {
      *b++ = *a++;
    }
    c->cibase->argc = len;
    c->prev = mrb->c;
    if (c->prev->fib) 
      mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
    mrb_write_barrier(mrb, (struct RBasic*)c->fib);
    c->status = MRB_FIBER_RUNNING;
    mrb->c = c;

    MARK_CONTEXT_MODIFY(c);
    return c->ci->proc->env->stack[0];
  }
  MARK_CONTEXT_MODIFY(c);
  c->prev = mrb->c;
  if (c->prev->fib) 
    mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
  c->status = MRB_FIBER_RUNNING;
  mrb->c = c;
  return fiber_result(mrb, a, len);
}
Beispiel #5
0
static mrb_value
fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume)
{
  struct mrb_context *c = fiber_check(mrb, self);
  mrb_callinfo *ci;

  for (ci = c->ci; ci >= c->cibase; ci--) {
    if (ci->acc < 0) {
      mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary");
    }
  }
  if (resume && c->status == MRB_FIBER_TRANSFERRED) {
    mrb_raise(mrb, E_FIBER_ERROR, "resuming transfered fiber");
  }
  if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMING) {
    mrb_raise(mrb, E_FIBER_ERROR, "double resume");
  }
  if (c->status == MRB_FIBER_TERMINATED) {
    mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
  }
  mrb->c->status = resume ? MRB_FIBER_RESUMING : MRB_FIBER_TRANSFERRED;
  c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
  if (c->status == MRB_FIBER_CREATED) {
    mrb_value *b = c->stack+1;
    mrb_value *e = b + len;

    while (b<e) {
      *b++ = *a++;
    }
    c->cibase->argc = len;
    if (c->prev->fib)
      mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
    mrb_write_barrier(mrb, (struct RBasic*)c->fib);
    c->status = MRB_FIBER_RUNNING;
    mrb->c = c;

    MARK_CONTEXT_MODIFY(c);
    return c->ci->proc->env->stack[0];
  }
  MARK_CONTEXT_MODIFY(c);
  if (c->prev->fib)
    mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib);
  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
  c->status = MRB_FIBER_RUNNING;
  mrb->c = c;
  return fiber_result(mrb, a, len);
}
Beispiel #6
0
static mrb_value
fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec)
{
  struct mrb_context *c = fiber_check(mrb, self);
  struct mrb_context *old_c = mrb->c;
  mrb_value value;

  fiber_check_cfunc(mrb, c);
  if (resume && c->status == MRB_FIBER_TRANSFERRED) {
    mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
  }
  if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
    mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
  }
  if (c->status == MRB_FIBER_TERMINATED) {
    mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
  }
  mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
  c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
  if (c->status == MRB_FIBER_CREATED) {
    mrb_value *b = c->stack+1;
    mrb_value *e = b + len;

    while (b<e) {
      *b++ = *a++;
    }
    c->cibase->argc = len;
    value = c->stack[0] = c->ci->proc->env->stack[0];
  }
  else {
    value = fiber_result(mrb, a, len);
  }
  mrb_write_barrier(mrb, (struct RBasic*)c->fib);
  c->status = MRB_FIBER_RUNNING;
  mrb->c = c;

  if (vmexec) {
    c->vmexec = TRUE;
    value = mrb_vm_exec(mrb, c->ci[-1].proc, c->ci->pc);
    mrb->c = old_c;
  }
  else {
    MARK_CONTEXT_MODIFY(c);
  }
  return value;
}
Beispiel #7
0
void fiber_save_errno(void)
{
	ACL_FIBER *curr;

	if (__thread_fiber == NULL)
		fiber_check();

	if ((curr = __thread_fiber->running) == NULL)
		curr = &__thread_fiber->original;

	if (curr->flag & FIBER_F_SAVE_ERRNO) {
		//curr->flag &= ~FIBER_F_SAVE_ERRNO;
		return;
	}

	if (__sys_errno != NULL)
		acl_fiber_set_errno(curr, *__sys_errno());
	else
		acl_fiber_set_errno(curr, errno);
}
Beispiel #8
0
/*
 *  call-seq:
 *     fiber.alive? -> true or false
 *
 *  Returns true if the fiber can still be resumed. After finishing
 *  execution of the fiber block this method will always return false.
 */
static mrb_value
fiber_alive_p(mrb_state *mrb, mrb_value self)
{
  struct mrb_context *c = fiber_check(mrb, self);
  return mrb_bool_value(c->status != MRB_FIBER_TERMINATED);
}
Beispiel #9
0
static ACL_FIBER *fiber_alloc(void (*fn)(ACL_FIBER *, void *),
	void *arg, size_t size)
{
	ACL_FIBER *fiber;
	sigset_t zero;
	union cc_arg carg;
	ACL_RING *head;

	fiber_check();

#define	APPL	ACL_RING_TO_APPL

	/* try to reuse the fiber memory in dead queue */
	head = acl_ring_pop_head(&__thread_fiber->dead);
	if (head == NULL) {
		fiber = (ACL_FIBER *) acl_mycalloc(1, sizeof(ACL_FIBER));
		fiber->buff = (char *) acl_mymalloc(size);
	} else if ((fiber = APPL(head, ACL_FIBER, me))->size < size)
		fiber->buff = (char *) acl_myrealloc(fiber->buff, size);
	else
		size = fiber->size;

	fiber->errnum = 0;
	fiber->fn     = fn;
	fiber->arg    = arg;
	fiber->size   = size;
	fiber->id     = ++__thread_fiber->idgen;
	fiber->flag   = 0;
	fiber->status = FIBER_STATUS_READY;

	carg.p = fiber;

	if (fiber->context == NULL)
		fiber->context = (ucontext_t *) acl_mymalloc(sizeof(ucontext_t));
	sigemptyset(&zero);
	sigprocmask(SIG_BLOCK, &zero, &fiber->context->uc_sigmask);

	if (getcontext(fiber->context) < 0)
		acl_msg_fatal("%s(%d), %s: getcontext error: %s",
			__FILE__, __LINE__, __FUNCTION__, acl_last_serror());

	fiber->context->uc_stack.ss_sp   = fiber->buff + 8;
	fiber->context->uc_stack.ss_size = fiber->size - 64;

#ifdef	USE_JMP
	fiber->context->uc_link = NULL;
#else
	fiber->context->uc_link = __thread_fiber->original.context;
#endif

#ifdef USE_VALGRIND
	/* avoding the valgrind's warning */
	fiber->vid = VALGRIND_STACK_REGISTER(fiber->context->uc_stack.ss_sp,
			fiber->context->uc_stack.ss_sp
			+ fiber->context->uc_stack.ss_size);
#endif
	makecontext(fiber->context, (void(*)(void)) fiber_start,
		2, carg.i[0], carg.i[1]);

	return fiber;
}
Beispiel #10
0
ACL_FIBER *acl_fiber_running(void)
{
	fiber_check();
	return __thread_fiber->running;
}